1 /*
2  * Copyright (C) 2010-2011 Dmitry Marakasov
3  *
4  * This file is part of glosm.
5  *
6  * glosm is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * glosm is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with glosm.  If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #include <glosm/GeometryGenerator.hh>
21 
22 #include <glosm/OsmDatasource.hh>
23 #include <glosm/Geometry.hh>
24 #include <glosm/GeometryOperations.hh>
25 #include <glosm/geomath.h>
26 
27 #include <list>
28 #include <cstdlib>
29 #include <cstdio>
30 
31 typedef std::list<Vector2i> VertexList;
32 typedef std::vector<Vector2i> VertexVector;
33 
CreateLines(Geometry & geom,const VertexVector & vertices,int z,const OsmDatasource::Way &)34 static void CreateLines(Geometry& geom, const VertexVector& vertices, int z, const OsmDatasource::Way& /*unused*/) {
35 	for (unsigned int i = 1; i < vertices.size(); ++i)
36 		geom.AddLine(Vector3i(vertices[i-1], z), Vector3i(vertices[i], z));
37 }
38 
CreateVerticalLines(Geometry & geom,const VertexVector & vertices,int minz,int maxz,const OsmDatasource::Way & way)39 static void CreateVerticalLines(Geometry& geom, const VertexVector& vertices, int minz, int maxz, const OsmDatasource::Way& way) {
40 	for (unsigned int i = way.Closed ? 1 : 0; i < vertices.size(); ++i)
41 		geom.AddLine(Vector3i(vertices[i], minz), Vector3i(vertices[i], maxz));
42 }
43 
CreateSmartVerticalLines(Geometry & geom,const VertexVector & vertices,int minz,int maxz,float minslope,const OsmDatasource::Way & way)44 static void CreateSmartVerticalLines(Geometry& geom, const VertexVector& vertices, int minz, int maxz, float minslope, const OsmDatasource::Way& way) {
45 	double cosminslope = cos(minslope/180.0*M_PI);
46 
47 	if (vertices.size() < 2)
48 		return CreateVerticalLines(geom, vertices, minz, maxz, way);
49 
50 	if (way.Closed) {
51 		for (unsigned int i = 0; i < vertices.size() - 1; ++i) {
52 			Vector3d to_prev = ToLocalMetric(vertices[i == 0 ? vertices.size() - 2 : i - 1], vertices[i]).Normalized();
53 			Vector3d to_next = ToLocalMetric(vertices[i + 1], vertices[i]).Normalized();
54 
55 			if (fabs(to_prev.DotProduct(to_next)) < cosminslope)
56 				geom.AddLine(Vector3i(vertices[i], minz), Vector3i(vertices[i], maxz));
57 		}
58 	} else {
59 		for (unsigned int i = 1; i < vertices.size() - 1; ++i) {
60 			Vector3d to_prev = ToLocalMetric(vertices[i - 1], vertices[i]).Normalized();
61 			Vector3d to_next = ToLocalMetric(vertices[i + 1], vertices[i]).Normalized();
62 
63 			if (fabs(to_prev.DotProduct(to_next)) < cosminslope)
64 				geom.AddLine(Vector3i(vertices[i], minz), Vector3i(vertices[i], maxz));
65 		}
66 		geom.AddLine(Vector3i(vertices.front(), minz), Vector3i(vertices.front(), maxz));
67 		geom.AddLine(Vector3i(vertices.back(), minz), Vector3i(vertices.back(), maxz));
68 	}
69 }
70 
CreateWalls(Geometry & geom,const VertexVector & vertices,int minz,int maxz,const OsmDatasource::Way &)71 static void CreateWalls(Geometry& geom, const VertexVector& vertices, int minz, int maxz, const OsmDatasource::Way& /*unused*/) {
72 	for (unsigned int i = 1; i < vertices.size(); ++i)
73 		geom.AddQuad(Vector3i(vertices[i-1], minz), Vector3i(vertices[i-1], maxz), Vector3i(vertices[i], maxz), Vector3i(vertices[i], minz));
74 }
75 
CreateWall(Geometry & geom,const VertexVector & vertices,int minz,int maxz,const OsmDatasource::Way &)76 static void CreateWall(Geometry& geom, const VertexVector& vertices, int minz, int maxz, const OsmDatasource::Way& /*unused*/) {
77 	for (unsigned int i = 1; i < vertices.size(); ++i) {
78 		geom.AddQuad(Vector3i(vertices[i-1], minz), Vector3i(vertices[i-1], maxz), Vector3i(vertices[i], maxz), Vector3i(vertices[i], minz));
79 		geom.AddQuad(Vector3i(vertices[i-1], maxz), Vector3i(vertices[i-1], minz), Vector3i(vertices[i], minz), Vector3i(vertices[i], maxz));
80 	}
81 }
82 
CreateArea(Geometry & geom,const VertexVector & vertices,bool revorder,int z,const OsmDatasource::Way & way)83 static void CreateArea(Geometry& geom, const VertexVector& vertices, bool revorder, int z, const OsmDatasource::Way& way) {
84 	if (vertices.size() < 3 || !way.Closed)
85 		return;
86 
87 	VertexList vert;
88 	for (VertexVector::const_iterator i = vertices.begin(); i != vertices.end(); ++i)
89 		vert.push_back(*i);
90 	vert.pop_back();
91 
92 	VertexList::iterator i0, i1, i2, o;
93 	i0 = vert.begin();
94 
95 	/* quick & dirty triangulation for roofs
96 	 * this should be rewritten to support sprips and/or fans,
97 	 * and there should be no possibility of infinite loop,
98 	 * as well as this iteration counter
99 	 */
100 	int iters = 1000;
101 	while (vert.size() >= 3) {
102 		if (++(i1 = i0) == vert.end())
103 			i1 = vert.begin();
104 		if (++(i2 = i1) == vert.end())
105 			i2 = vert.begin();
106 
107 		osmlong_t area = 0;
108 		area += (osmlong_t)i0->x*(osmlong_t)i1->y - (osmlong_t)i0->y*(osmlong_t)i1->x;
109 		area += (osmlong_t)i1->x*(osmlong_t)i2->y - (osmlong_t)i1->y*(osmlong_t)i2->x;
110 		area += (osmlong_t)i2->x*(osmlong_t)i0->y - (osmlong_t)i2->y*(osmlong_t)i0->x;
111 
112 		bool ok = true;
113 		o = i2;
114 		while (1) {
115 			if (++o == vert.end())
116 				o = vert.begin();
117 
118 			if (o == i0)
119 				break;
120 
121 			if (Vector2l(*o).IsInTriangle(*i0, *i1, *i2)) {
122 				ok = false;
123 				break;
124 			}
125 		}
126 
127 		if ((area < 0) && ok) {
128 			if (revorder)
129 				geom.AddTriangle(Vector3i(*i0, z), Vector3i(*i1, z), Vector3i(*i2, z));
130 			else
131 				geom.AddTriangle(Vector3i(*i0, z), Vector3i(*i2, z), Vector3i(*i1, z));
132 
133 			vert.erase(i1);
134 		}
135 
136 		if (++i0 == vert.end())
137 			i0 = vert.begin();
138 
139 		if (--iters == 0) {
140 			/* FIXME: add way ID here, lacks interface to datasource */
141 			fprintf(stderr, "warning: triangulation failed: giving up on %d/%d points\n", vert.size(), vertices.size());
142 			return;
143 		}
144 	}
145 }
146 
CreateRoof(Geometry & geom,const VertexVector & vertices,int z,const OsmDatasource::Way & way)147 static void CreateRoof(Geometry& geom, const VertexVector& vertices, int z, const OsmDatasource::Way& way) {
148 	float slope = 30.0;
149 	bool along = true;
150 
151 	OsmDatasource::TagsMap::const_iterator shape, tag;
152 
153 	if ((tag = way.Tags.find("building:roof:angle")) != way.Tags.end())
154 		slope = strtof(tag->second.c_str(), NULL);
155 	if ((tag = way.Tags.find("building:roof:orientation")) != way.Tags.end() && tag->second == "across")
156 		along = false;
157 
158 	std::vector<Vector3i> vert;
159 	vert.reserve(vertices.size());
160 	for (VertexVector::const_iterator i = vertices.begin(); i != vertices.end(); ++i)
161 		vert.push_back(Vector3i(*i, z));
162 
163 	if (vert.size() > 3 && way.Closed &&
164 				(shape = way.Tags.find("building:roof:shape")) != way.Tags.end() &&
165 				(shape->second == "pyramidal" || shape->second == "conical")
166 			) {
167 		/* calculate center */
168 		Vector3l center;
169 		for (unsigned int i = 0; i < vert.size() - 1; i++)
170 			center += Vector3i(vert[i]);
171 		center /= vert.size() - 1;
172 
173 		/* calculate mean face length */
174 		float facelength = 0.0;
175 		for (unsigned int i = 0; i < vert.size() - 1; i++)
176 			facelength += (ToLocalMetric(vert[i], center)/2.0 + ToLocalMetric(vert[i+1], center)/2.0).Length();
177 		facelength /= vert.size() - 1;
178 
179 		center.z += (tan(slope/180.0*M_PI) * facelength) * GEOM_UNITSINMETER;
180 
181 		for (unsigned int i = 0; i < vert.size() - 1; i++) {
182 			geom.AddTriangle(vert[i], center, vert[i+1]);
183 			geom.AddLine(vert[i], center);
184 		}
185 		return;
186 	}
187 
188 	/* only 4-vert buildings are supported for other types, yet */
189 	if (vert.size() == 5 && way.Closed && (shape = way.Tags.find("building:roof:shape")) != way.Tags.end()) {
190 		float length1 = ToLocalMetric(vert[0], vert[1]).Length();
191 		float length2 = ToLocalMetric(vert[1], vert[2]).Length();
192 
193 		if (shape->second == "pyramidal") {
194 			Vector3i center = ((Vector3l)vert[0] + (Vector3l)vert[1] + (Vector3l)vert[2] + (Vector3l)vert[3]) / 4;
195 			center.z += (tan(slope/180.0*M_PI) * std::min(length1, length2) * 0.5) * GEOM_UNITSINMETER;
196 
197 			for (int i = 0; i < 4; i++) {
198 				geom.AddTriangle(vert[i], center, vert[i+1]);
199 				geom.AddLine(vert[i], center);
200 			}
201 			return;
202 		} else if (shape->second == "pitched") {
203 			if (!!(length1 < length2) ^ !along) {
204 				osmint_t height = (tan(slope/180.0*M_PI) * length1 * 0.5) * GEOM_UNITSINMETER;
205 
206 				Vector3i center1 = ((Vector3l)vert[0] + (Vector3l)vert[1])/2;
207 				Vector3i center2 = ((Vector3l)vert[2] + (Vector3l)vert[3])/2;
208 
209 				center1.z += height;
210 				center2.z += height;
211 
212 				geom.AddTriangle(vert[0], center1, vert[1]);
213 				geom.AddTriangle(vert[2], center2, vert[3]);
214 				geom.AddQuad(vert[2], vert[1], center1, center2);
215 				geom.AddQuad(vert[0], vert[3], center2, center1);
216 
217 				geom.AddLine(vert[0], center1); geom.AddLine(center1, vert[1]);
218 				geom.AddLine(vert[2], center2); geom.AddLine(center2, vert[3]);
219 				geom.AddLine(center1, center2);
220 			} else {
221 				osmint_t height = (tan(slope/180.0*M_PI) * length2 * 0.5) * GEOM_UNITSINMETER;
222 
223 				Vector3i center1 = ((Vector3l)vert[1] + (Vector3l)vert[2])/2;
224 				Vector3i center2 = ((Vector3l)vert[0] + (Vector3l)vert[3])/2;
225 
226 				center1.z += height;
227 				center2.z += height;
228 
229 				geom.AddTriangle(vert[1], center1, vert[2]);
230 				geom.AddTriangle(vert[3], center2, vert[0]);
231 				geom.AddQuad(vert[1], vert[0], center2, center1);
232 				geom.AddQuad(vert[3], vert[2], center1, center2);
233 
234 				geom.AddLine(vert[1], center1); geom.AddLine(center1, vert[2]);
235 				geom.AddLine(vert[3], center2); geom.AddLine(center2, vert[0]);
236 				geom.AddLine(center1, center2);
237 			}
238 			return;
239 		} else if (shape->second == "hipped") {
240 			if (length1 < length2) {
241 				osmint_t height = (tan(slope/180.0*M_PI) * length1 * 0.5) * GEOM_UNITSINMETER;
242 
243 				Vector3i center1 = ((Vector3l)vert[0] + (Vector3l)vert[1])/2;
244 				Vector3i center2 = ((Vector3l)vert[2] + (Vector3l)vert[3])/2;
245 
246 				Vector3i delta = FromLocalMetric(ToLocalMetric(center2, center1).Normalized() * ToLocalMetric(vert[0], center1).Length(), center1) - center1;
247 
248 				center1 += delta;
249 				center2 -= delta;
250 
251 				center1.z += height;
252 				center2.z += height;
253 
254 				geom.AddTriangle(vert[0], center1, vert[1]);
255 				geom.AddTriangle(vert[2], center2, vert[3]);
256 				geom.AddQuad(vert[2], vert[1], center1, center2);
257 				geom.AddQuad(vert[0], vert[3], center2, center1);
258 
259 				geom.AddLine(vert[0], center1); geom.AddLine(center1, vert[1]);
260 				geom.AddLine(vert[2], center2); geom.AddLine(center2, vert[3]);
261 				geom.AddLine(center1, center2);
262 			} else {
263 				osmint_t height = (tan(slope/180.0*M_PI) * length2 * 0.5) * GEOM_UNITSINMETER;
264 
265 				Vector3i center1 = ((Vector3l)vert[1] + (Vector3l)vert[2])/2;
266 				Vector3i center2 = ((Vector3l)vert[0] + (Vector3l)vert[3])/2;
267 
268 				Vector3i delta = FromLocalMetric(ToLocalMetric(center2, center1).Normalized() * ToLocalMetric(vert[1], center1).Length(), center1) - center1;
269 
270 				center1 += delta;
271 				center2 -= delta;
272 
273 				center1.z += height;
274 				center2.z += height;
275 
276 				geom.AddTriangle(vert[1], center1, vert[2]);
277 				geom.AddTriangle(vert[3], center2, vert[0]);
278 				geom.AddQuad(vert[1], vert[0], center2, center1);
279 				geom.AddQuad(vert[3], vert[2], center1, center2);
280 
281 				geom.AddLine(vert[1], center1); geom.AddLine(center1, vert[2]);
282 				geom.AddLine(vert[3], center2); geom.AddLine(center2, vert[0]);
283 				geom.AddLine(center1, center2);
284 			}
285 			return;
286 		} else if (shape->second == "crosspitched") {
287 			int height = (tan(slope/180.0*M_PI) * std::min(length1, length2) * 0.5) * GEOM_UNITSINMETER;
288 
289 			Vector3i center = ((Vector3l)vert[0] + (Vector3l)vert[1] + (Vector3l)vert[2] + (Vector3l)vert[3]) / 4;
290 			center.z += height;
291 
292 			for (int i = 0; i < 4; ++i) {
293 				Vector3i sidecenter = ((Vector3l)vert[i] + (Vector3l)vert[i+1]) / 2;
294 				sidecenter.z += height;
295 
296 				geom.AddTriangle(vert[i], center, sidecenter);
297 				geom.AddTriangle(center, vert[i+1], sidecenter);
298 				geom.AddTriangle(vert[i+1], vert[i], sidecenter);
299 
300 				geom.AddLine(sidecenter, center);
301 				geom.AddLine(sidecenter, vert[i]);
302 				geom.AddLine(sidecenter, vert[i+1]);
303 				geom.AddLine(vert[i], center);
304 			}
305 			return;
306 		}
307 	}
308 
309 	/* fallback - flat roof */
310 	return CreateArea(geom, vertices, false, z, way);
311 }
312 
CreateRoad(Geometry & geom,const VertexVector & vertices,float width,const OsmDatasource::Way &)313 static void CreateRoad(Geometry& geom, const VertexVector& vertices, float width, const OsmDatasource::Way& /*unused*/) {
314 	if (vertices.size() < 2)
315 		return;
316 
317 	VertexVector::const_iterator prev = vertices.end();
318 	VertexVector::const_iterator next;
319 	Vector2i prev_points[2];
320 	Vector2i new_points[2];
321 	for (VertexVector::const_iterator i = vertices.begin(); i != vertices.end(); i++) {
322 		++(next = i);
323 
324 		Vector3d to_prev, to_next;
325 
326 		if (next != vertices.end()) {
327 			to_next = ToLocalMetric(*next, *i).Normalized();
328 			if (prev != vertices.end())
329 				to_prev = ToLocalMetric(*prev, *i).Normalized();
330 			else
331 				to_prev = -to_next;
332 		} else {
333 			to_prev = ToLocalMetric(*prev, *i).Normalized();
334 			to_next = -to_prev;
335 		}
336 
337 		Vector3d normside = to_next.CrossProduct(Vector3d(0.0, 0.0, 1.0));
338 		Vector3d side = normside * width / 2.0;
339 
340 		Vector3d bisect = to_prev + to_next;
341 		double cosangle = to_next.DotProduct(to_prev);
342 
343 		if (cosangle > 0.99) {
344 			/* FIXME: add way ID here, lacks interface to datasource */
345 			fprintf(stderr, "warning: too sharp road turn (cos=%f), likely data error\n", cosangle);
346 			prev = i;
347 			continue;
348 		}
349 
350 		if (bisect.Length() < 0.001) { /* constant == sin(alpha/2), where alpha is minimal angle to consider joint non-straight */
351 			/* almost straight segment, just use normals */
352 			new_points[0] = FromLocalMetric(-side, *i);
353 			new_points[1] = FromLocalMetric(side, *i);
354 		} else {
355 			bisect = bisect.Normalized() * (width / 2.0) / sin(acos(cosangle) / 2.0);
356 
357 			if (bisect.Normalized().DotProduct(normside) < 0.0) {
358 				new_points[0] = FromLocalMetric(bisect, *i);
359 				new_points[1] = FromLocalMetric(-bisect, *i);
360 			} else {
361 				new_points[0] = FromLocalMetric(-bisect, *i);
362 				new_points[1] = FromLocalMetric(bisect, *i);
363 			}
364 		}
365 
366 		if (prev != vertices.end())
367 			geom.AddQuad(prev_points[0], prev_points[1], new_points[1], new_points[0]);
368 
369 		prev_points[0] = new_points[0];
370 		prev_points[1] = new_points[1];
371 
372 		prev = i;
373 	}
374 }
375 
GetMaxHeight(const OsmDatasource::Way & way)376 static float GetMaxHeight(const OsmDatasource::Way& way) {
377 	OsmDatasource::TagsMap::const_iterator building, tag;
378 
379 	if ((tag = way.Tags.find("building:part:height")) != way.Tags.end()) {
380 		/* building:part:height is topmost precedence (hack for Ostankino tower) */
381 		return strtof(tag->second.c_str(), NULL);
382 	} else if ((tag = way.Tags.find("height")) != way.Tags.end()) {
383 		/* explicit height - topmost precedence in all other cases */
384 		return strtof(tag->second.c_str(), NULL);
385 	} else if ((tag = way.Tags.find("building:levels")) != way.Tags.end()) {
386 		/* count level heights as 3 meters */
387 		int levels = strtol(tag->second.c_str(), NULL, 10);
388 		float h = 3.0 * levels;
389 
390 		/* also add 1 meter for basement for short buildings
391 		 * (except for garages which doesn't have one) - should work
392 		 * well in rural areas */
393 		if (levels == 1 && (building = way.Tags.find("building")) != way.Tags.end() && building->second != "garages" && building->second != "garage")
394 			h += 1.0;
395 
396 		return h;
397 	}
398 
399 	return 0.0;
400 }
401 
GetMinHeight(const OsmDatasource::Way & way)402 static float GetMinHeight(const OsmDatasource::Way& way) {
403 	OsmDatasource::TagsMap::const_iterator building, tag, tag1;
404 
405 	if ((tag = way.Tags.find("min_height")) != way.Tags.end()) {
406 		/* explicit height - topmost precedence in all other cases */
407 		return strtof(tag->second.c_str(), NULL);
408 	} else if ((tag = way.Tags.find("building:min_level")) != way.Tags.end() || (tag = way.Tags.find("building:skipped_levels")) != way.Tags.end()) {
409 		/* count level heights as 3 meters */
410 		float h = 3.0 * strtol(tag->second.c_str(), NULL, 10);
411 
412 		/* in building:min_level scheme, levels are counted from zero, which may be fixed by building:ground_level... */
413 		if (tag->first == "building:min_level" && (tag1 = way.Tags.find("building:ground_level")) != way.Tags.end())
414 			h -= 3.0 * strtol(tag1->second.c_str(), NULL, 10);
415 		/* ...while in my proposal (building:skipped_levels) everythng just works */
416 
417 		return h;
418 	}
419 
420 	return 0.0;
421 }
422 
GetHighwayLanes(const std::string & highway,const OsmDatasource::Way & way)423 static int GetHighwayLanes(const std::string& highway, const OsmDatasource::Way& way) {
424 	OsmDatasource::TagsMap::const_iterator tag;
425 
426 	/* explicitely tagged lanes have top */
427 	if ((tag = way.Tags.find("lanes")) != way.Tags.end())
428 		return strtol(tag->second.c_str(), NULL, 10);
429 
430 	bool oneway = false;
431 	if ((tag = way.Tags.find("oneway")) != way.Tags.end() && tag->second != "no")
432 		oneway = true;
433 
434 	/* motorway assumes one-way */
435 	if (highway == "motorway" || highway == "motorway_link")
436 		oneway = true;
437 
438 	if (highway == "service" || highway == "track") {
439 		return 1;
440 	} else if (highway == "residential") {
441 		return 2;
442 	} else {
443 		return oneway ? 2 : 4;
444 	}
445 }
446 
GetHighwayWidth(const std::string & highway,const OsmDatasource::Way & way)447 static float GetHighwayWidth(const std::string& highway, const OsmDatasource::Way& way) {
448 	OsmDatasource::TagsMap::const_iterator tag;
449 
450 	/* explicitely tagged lanes have top */
451 	if ((tag = way.Tags.find("width")) != way.Tags.end())
452 		return strtof(tag->second.c_str(), NULL);
453 
454 	if (highway == "path") {
455 		return 0.5f;
456 	} else if (highway == "footway" || highway == "steps") {
457 		return 2.0f;
458 	} else if (highway == "pedestrian") {
459 		return 3.0f;
460 	} else {
461 		return GetHighwayLanes(highway, way) * 3.5f; /* likely 4 is closer to truth */
462 	}
463 }
464 
WayDispatcher(Geometry & geom,const OsmDatasource & datasource,int flags,const OsmDatasource::Way & way)465 static void WayDispatcher(Geometry& geom, const OsmDatasource& datasource, int flags, const OsmDatasource::Way& way) {
466 	osmint_t minz = GetMinHeight(way) * GEOM_UNITSINMETER;
467 	osmint_t maxz = GetMaxHeight(way) * GEOM_UNITSINMETER;
468 
469 	if (minz < 0)
470 		minz = 0;
471 	if (maxz < minz) {
472 		fprintf(stderr, "warning: max height < min height for object\n");
473 		maxz = minz = 0;
474 	}
475 
476 	OsmDatasource::TagsMap::const_iterator t;
477 
478 	VertexVector vertices;
479 	vertices.reserve(way.Nodes.size());
480 
481 	if (way.Clockwise)
482 		for (OsmDatasource::Way::NodesList::const_iterator n = way.Nodes.begin(); n != way.Nodes.end(); ++n)
483 			vertices.push_back(datasource.GetNode(*n).Pos);
484 	else
485 		for (OsmDatasource::Way::NodesList::const_reverse_iterator n = way.Nodes.rbegin(); n != way.Nodes.rend(); ++n)
486 			vertices.push_back(datasource.GetNode(*n).Pos);
487 
488 	/* dispatch */
489 	if ((way.Tags.find("building") != way.Tags.end() || way.Tags.find("building:part") != way.Tags.end()) && minz != maxz) {
490 		if (flags & GeometryDatasource::DETAIL) {
491 			CreateWalls(geom, vertices, minz, maxz, way);
492 			CreateRoof(geom, vertices, maxz, way);
493 
494 			CreateLines(geom, vertices, minz, way);
495 			CreateLines(geom, vertices, maxz, way);
496 			CreateSmartVerticalLines(geom, vertices, minz, maxz, 5.0, way);
497 
498 			if (minz > 1) {
499 				CreateArea(geom, vertices, true, minz, way);
500 				CreateLines(geom, vertices, minz, way);
501 			}
502 		}
503 	} else if ((t = way.Tags.find("man_made")) != way.Tags.end() && (t->second == "tower" || t->second == "chimney") && minz != maxz) {
504 		if (flags & GeometryDatasource::DETAIL) {
505 			CreateWalls(geom, vertices, minz, maxz, way);
506 			CreateArea(geom, vertices, false, maxz, way);
507 
508 			CreateLines(geom, vertices, minz, way);
509 			CreateLines(geom, vertices, maxz, way);
510 			CreateSmartVerticalLines(geom, vertices, minz, maxz, 5.0, way);
511 		}
512 	} else if (way.Tags.find("barrier") != way.Tags.end()) {
513 		if (flags & GeometryDatasource::DETAIL) {
514 			if (maxz == minz)
515 				maxz += 2 * GEOM_UNITSINMETER;
516 			CreateWall(geom, vertices, minz, maxz, way);
517 
518 			CreateLines(geom, vertices, minz, way);
519 			CreateLines(geom, vertices, maxz, way);
520 			CreateVerticalLines(geom, vertices, minz, maxz, way);
521 		}
522 	} else if ((t = way.Tags.find("highway")) != way.Tags.end()) {
523 		if (flags & GeometryDatasource::DETAIL) {
524 			OsmDatasource::TagsMap::const_iterator t1;
525 
526 			if ((t1 = way.Tags.find("area")) != way.Tags.end() && t1->second != "no") {
527 				/* area */
528 				CreateArea(geom, vertices, false, 0, way);
529 			} else {
530 				CreateRoad(geom, vertices, GetHighwayWidth(t->second, way), way);
531 			}
532 		} else if ((flags & GeometryDatasource::GROUND) && (
533 				t->second == "motorway" || t->second == "motorway_link" ||
534 				t->second == "trunk" || t->second == "trunk_link" ||
535 				t->second == "primary" || t->second == "primary_link" ||
536 				t->second == "secondary" || t->second == "secondary_link" ||
537 				t->second == "tertiary")) {
538 			CreateLines(geom, vertices, minz, way);
539 		}
540 	} else if ((t = way.Tags.find("railway")) != way.Tags.end() && (t->second == "rail")) {
541 		if (flags & GeometryDatasource::DETAIL) {
542 			CreateLines(geom, vertices, minz, way);
543 		} else if (flags & GeometryDatasource::GROUND) {
544 			if (t->second == "rail")
545 				CreateLines(geom, vertices, minz, way);
546 		}
547 	} else if ((t = way.Tags.find("boundary")) != way.Tags.end() && (t->second == "administrative")) {
548 		if (flags & GeometryDatasource::GROUND)
549 			CreateLines(geom, vertices, minz, way);
550 	} else if ((t = way.Tags.find("waterway")) != way.Tags.end()) {
551 		if (flags & GeometryDatasource::GROUND)
552 			CreateLines(geom, vertices, minz, way);
553 	} else if ((t = way.Tags.find("natural")) != way.Tags.end()) {
554 		if (flags & GeometryDatasource::GROUND)
555 			CreateLines(geom, vertices, minz, way);
556 	} else if ((t = way.Tags.find("landuse")) != way.Tags.end()) {
557 		if (flags & GeometryDatasource::GROUND)
558 			CreateLines(geom, vertices, minz, way);
559 	} else {
560 		if (flags & GeometryDatasource::DETAIL)
561 			CreateLines(geom, vertices, minz, way);
562 	}
563 }
564 
GeometryGenerator(const OsmDatasource & datasource)565 GeometryGenerator::GeometryGenerator(const OsmDatasource& datasource) : datasource_(datasource) {
566 }
567 
GetGeometry(Geometry & geom,const BBoxi & bbox,int flags) const568 void GeometryGenerator::GetGeometry(Geometry& geom, const BBoxi& bbox, int flags) const {
569 	std::vector<OsmDatasource::Way> ways;
570 
571 	/* safe bbox is a bit wider than requested one to be sure
572 	 * all ways are included, even those which have width */
573 	float extra_width = 24.0; /* still may be not sufficient, e.g. very wide roads */
574 	BBoxi safe_bbox = BBoxi(
575 			FromLocalMetric(-Vector2d(extra_width, extra_width), bbox.GetBottomLeft()),
576 			FromLocalMetric(Vector2d(extra_width, extra_width), bbox.GetTopRight())
577 		);
578 	datasource_.GetWays(ways, safe_bbox);
579 
580 	Geometry temp;
581 
582 	for (std::vector<OsmDatasource::Way>::const_iterator w = ways.begin(); w != ways.end(); ++w)
583 		WayDispatcher(temp, datasource_, flags, *w);
584 
585 	geom.AppendCropped(temp, bbox);
586 }
587 
GetCenter() const588 Vector2i GeometryGenerator::GetCenter() const {
589 	return datasource_.GetCenter();
590 }
591 
GetBBox() const592 BBoxi GeometryGenerator::GetBBox() const {
593 	return datasource_.GetBBox();
594 }
595