1#include "RegionsHierarchy.h"
2
3#include <IO/WriteHelpers.h>
4#include <Poco/Exception.h>
5#include <Poco/Util/Application.h>
6#include <common/logger_useful.h>
7#include "GeodataProviders/IHierarchiesProvider.h"
8
9
10namespace DB
11{
12namespace ErrorCodes
13{
14 extern const int INCORRECT_DATA;
15}
16}
17
18
19RegionsHierarchy::RegionsHierarchy(IRegionsHierarchyDataSourcePtr data_source_) : data_source(data_source_)
20{
21}
22
23
24void RegionsHierarchy::reload()
25{
26 Logger * log = &Logger::get("RegionsHierarchy");
27
28 if (!data_source->isModified())
29 return;
30
31 LOG_DEBUG(log, "Reloading regions hierarchy");
32
33 const size_t initial_size = 10000;
34 const size_t max_size = 15000000;
35
36 RegionParents new_parents(initial_size);
37 RegionParents new_city(initial_size);
38 RegionParents new_country(initial_size);
39 RegionParents new_area(initial_size);
40 RegionParents new_district(initial_size);
41 RegionParents new_continent(initial_size);
42 RegionParents new_top_continent(initial_size);
43 RegionPopulations new_populations(initial_size);
44 RegionDepths new_depths(initial_size);
45 RegionTypes types(initial_size);
46
47 RegionID max_region_id = 0;
48
49 auto regions_reader = data_source->createReader();
50
51 RegionEntry region_entry;
52 while (regions_reader->readNext(region_entry))
53 {
54 if (region_entry.id > max_region_id)
55 {
56 if (region_entry.id > max_size)
57 throw DB::Exception(
58 "Region id is too large: " + DB::toString(region_entry.id) + ", should be not more than " + DB::toString(max_size),
59 DB::ErrorCodes::INCORRECT_DATA);
60
61 max_region_id = region_entry.id;
62
63 while (region_entry.id >= new_parents.size())
64 {
65 new_parents.resize(new_parents.size() * 2);
66 new_populations.resize(new_parents.size());
67 types.resize(new_parents.size());
68 }
69 }
70
71 new_parents[region_entry.id] = region_entry.parent_id;
72 new_populations[region_entry.id] = region_entry.population;
73 types[region_entry.id] = region_entry.type;
74 }
75
76 new_parents.resize(max_region_id + 1);
77 new_city.resize(max_region_id + 1);
78 new_country.resize(max_region_id + 1);
79 new_area.resize(max_region_id + 1);
80 new_district.resize(max_region_id + 1);
81 new_continent.resize(max_region_id + 1);
82 new_top_continent.resize(max_region_id + 1);
83 new_populations.resize(max_region_id + 1);
84 new_depths.resize(max_region_id + 1);
85 types.resize(max_region_id + 1);
86
87 /// prescribe the cities and countries for the regions
88 for (RegionID i = 0; i <= max_region_id; ++i)
89 {
90 if (types[i] == RegionType::City)
91 new_city[i] = i;
92
93 if (types[i] == RegionType::Area)
94 new_area[i] = i;
95
96 if (types[i] == RegionType::District)
97 new_district[i] = i;
98
99 if (types[i] == RegionType::Country)
100 new_country[i] = i;
101
102 if (types[i] == RegionType::Continent)
103 {
104 new_continent[i] = i;
105 new_top_continent[i] = i;
106 }
107
108 RegionDepth depth = 0;
109 RegionID current = i;
110 while (true)
111 {
112 ++depth;
113
114 if (depth == std::numeric_limits<RegionDepth>::max())
115 throw Poco::Exception(
116 "Logical error in regions hierarchy: region " + DB::toString(current) + " possible is inside infinite loop");
117
118 current = new_parents[current];
119 if (current == 0)
120 break;
121
122 if (current > max_region_id)
123 throw Poco::Exception(
124 "Logical error in regions hierarchy: region " + DB::toString(current) + " (specified as parent) doesn't exist");
125
126 if (types[current] == RegionType::City)
127 new_city[i] = current;
128
129 if (types[current] == RegionType::Area)
130 new_area[i] = current;
131
132 if (types[current] == RegionType::District)
133 new_district[i] = current;
134
135 if (types[current] == RegionType::Country)
136 new_country[i] = current;
137
138 if (types[current] == RegionType::Continent)
139 {
140 if (!new_continent[i])
141 new_continent[i] = current;
142 new_top_continent[i] = current;
143 }
144 }
145
146 new_depths[i] = depth;
147 }
148
149 parents.swap(new_parents);
150 country.swap(new_country);
151 city.swap(new_city);
152 area.swap(new_area);
153 district.swap(new_district);
154 continent.swap(new_continent);
155 top_continent.swap(new_top_continent);
156 populations.swap(new_populations);
157 depths.swap(new_depths);
158}
159