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/MercatorProjection.hh>
21 #include <glosm/PreloadedXmlDatasource.hh>
22 #include <glosm/GeometryGenerator.hh>
23 #include <glosm/GeometryLayer.hh>
24 #include <glosm/OrthoViewer.hh>
25 #include <glosm/geomath.h>
26 
27 #include "PBuffer.hh"
28 #include "PixelBuffer.hh"
29 #include "PngWriter.hh"
30 
31 #include <GL/glx.h>
32 #include <X11/Xlib.h>
33 #include <getopt.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <sys/stat.h>
37 #include <sys/time.h>
38 
39 #include <cstdio>
40 
41 struct LevelInfo {
42 	int tiling;
43 	int flags;
44 
operator !=LevelInfo45 	bool operator!= (const LevelInfo& other) const {
46 		return tiling != other.tiling || flags != other.flags;
47 	}
48 };
49 
50 static LevelInfo LevelInfos[] = {
51 	{ 0, GeometryDatasource::GROUND }, /* 0 */
52 	{ 1, GeometryDatasource::GROUND }, /* 1 */
53 	{ 2, GeometryDatasource::GROUND }, /* 2 */
54 	{ 3, GeometryDatasource::GROUND }, /* 3 */
55 	{ 4, GeometryDatasource::GROUND }, /* 4 */
56 	{ 5, GeometryDatasource::GROUND }, /* 5 */
57 	{ 6, GeometryDatasource::GROUND }, /* 6 */
58 	{ 7, GeometryDatasource::GROUND }, /* 7 */
59 	{ 8, GeometryDatasource::GROUND }, /* 8 */
60 	{ 9, GeometryDatasource::GROUND }, /* 9 */
61 	{ 10, GeometryDatasource::GROUND }, /* 10 */
62 	{ 11, GeometryDatasource::EVERYTHING }, /* 11 */
63 	{ 12, GeometryDatasource::EVERYTHING }, /* 12 */
64 	{ 12, GeometryDatasource::EVERYTHING }, /* 13 */
65 	{ 12, GeometryDatasource::EVERYTHING }, /* 14 */
66 	{ 12, GeometryDatasource::EVERYTHING }, /* 15 */
67 	{ 13, GeometryDatasource::EVERYTHING }, /* 16 */
68 	{ 13, GeometryDatasource::EVERYTHING }, /* 17 */
69 	{ 14, GeometryDatasource::EVERYTHING }, /* 18 */
70 };
71 
usage(const char * progname)72 void usage(const char* progname) {
73 	fprintf(stderr, "Usage: %s [-0123456789] [-s skew] [-z minzoom] [-Z maxzoom] -x minlon -X maxlon -y minlat -Y maxlat infile.osm outdir\n", progname);
74 	exit(1);
75 }
76 
RenderTiles(PBuffer & pbuffer,OrthoViewer & viewer,GeometryLayer & layer,const char * target,float minlon,float minlat,float maxlon,float maxlat,int minzoom,int maxzoom,int pnglevel)77 int RenderTiles(PBuffer& pbuffer, OrthoViewer& viewer, GeometryLayer& layer, const char* target, float minlon, float minlat, float maxlon, float maxlat, int minzoom, int maxzoom, int pnglevel) {
78 	int x, y, zoom, ntiles = 0;
79 	PixelBuffer pixels(256, 256, 3);
80 
81 	char path[FILENAME_MAX];
82 	snprintf(path, sizeof(path), "%s", target);
83 	mkdir(path, 0777);
84 	for (zoom = minzoom; zoom <= maxzoom; zoom++) {
85 		layer.SetLevel(LevelInfos[zoom].tiling);
86 		layer.SetFlags(LevelInfos[zoom].flags);
87 		if (zoom > 0 && LevelInfos[zoom-1] != LevelInfos[zoom])
88 			layer.Clear();
89 
90 		int minxtile = (int)((minlon + 180.0)/360.0*powf(2.0, zoom));
91 		int maxxtile = (int)((maxlon + 180.0)/360.0*powf(2.0, zoom));
92 		int minytile = (int)((-mercator(maxlat/180.0*M_PI)/M_PI*180.0 + 180.0)/360.0*powf(2.0, zoom));
93 		int maxytile = (int)((-mercator(minlat/180.0*M_PI)/M_PI*180.0 + 180.0)/360.0*powf(2.0, zoom));
94 
95 		snprintf(path, sizeof(path), "%s/%d", target, zoom);
96 		mkdir(path, 0777);
97 		for (x = minxtile; x <= maxxtile; ++x) {
98 			snprintf(path, sizeof(path), "%s/%d/%d", target, zoom, x);
99 			mkdir(path, 0777);
100 			for (y = minytile; y <= maxytile; ++y) {
101 				snprintf(path, sizeof(path), "%s/%d/%d/%d.png", target, zoom, x, y);
102 
103 				BBoxi bbox = BBoxi::ForMercatorTile(zoom, x, y);
104 				viewer.SetBBox(bbox);
105 
106 				BBoxi request_bbox = bbox;
107 				/* expand request 1km down for skewed buildings to show correctly
108 				 * that is, we assume maximum object height of 1km */
109 
110 				/* @todo take skew into account */
111 				request_bbox.bottom -= 1000.0 / WGS84_EARTH_EQ_LENGTH * 360.0 * GEOM_UNITSINDEGREE;
112 
113 				glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
114 				layer.GarbageCollect();
115 				layer.LoadArea(request_bbox, TileManager::SYNC);
116 				layer.Render(viewer);
117 				glFinish();
118 
119 				pbuffer.GetPixels(pixels, 0, 0);
120 
121 				PngWriter writer(path, 256, 256, pnglevel);
122 				writer.WriteImage(pixels, 0, 0);
123 
124 				ntiles++;
125 			}
126 		}
127 	}
128 
129 	return ntiles;
130 }
131 
real_main(int argc,char ** argv)132 int real_main(int argc, char** argv) {
133 	const char* progname = argv[0];
134 
135 	int pnglevel = 6;
136 
137 	float minlat = 0.0f, maxlat = 0.0f, minlon = 0.0f, maxlon = 0.0f;
138 	int minzoom = 0, maxzoom = 18;
139 
140 	float skew = 1.0f;
141 
142 	int c;
143 	while ((c = getopt(argc, argv, "0123456789s:z:Z:x:X:y:Y:")) != -1) {
144 		switch (c) {
145 		case '0': case '1': case '2': case '3': case '4':
146 		case '5': case '6': case '7': case '8': case '9':
147 				  pnglevel = c - '0';
148 				  break;
149 		case 's': skew = strtof(optarg, NULL); break;
150 		case 'z': minzoom = strtol(optarg, NULL, 10); break;
151 		case 'Z': maxzoom = strtol(optarg, NULL, 10); break;
152 		case 'x': minlon = strtof(optarg, NULL); break;
153 		case 'X': maxlon = strtof(optarg, NULL); break;
154 		case 'y': minlat = strtof(optarg, NULL); break;
155 		case 'Y': maxlat = strtof(optarg, NULL); break;
156 		default:
157 			usage(progname);
158 		}
159 	}
160 
161 	argc -= optind;
162 	argv += optind;
163 
164 	if (minlon < -180.0f || maxlon > 180.0 || minlon > maxlon)
165 		usage(progname);
166 	if (minlat < -85.0f || maxlat > 85.0 || minlat > maxlat)
167 		usage(progname);
168 	if (minzoom < 0 || minzoom > maxzoom)
169 		usage(progname);
170 	if (skew <= 0.0)
171 		usage(progname);
172 	if (argc != 2)
173 		usage(progname);
174 
175 	/* OpenGL init */
176 	PBuffer pbuffer(256, 256, 4);
177 	glClearColor(0.5, 0.5, 0.5, 0.0);
178 
179 	/* glosm init */
180 	OrthoViewer viewer;
181 	viewer.SetSkew(skew);
182 	PreloadedXmlDatasource osm_datasource;
183 
184 	fprintf(stderr, "Loading OSM data...\n");
185 	osm_datasource.Load(argv[0]);
186 
187 	fprintf(stderr, "Creating geometry...\n");
188 	GeometryGenerator geometry_generator(osm_datasource);
189 
190 	GeometryLayer layer(MercatorProjection(), geometry_generator);
191 	layer.SetSizeLimit(128*1024*1024);
192 
193 	/* Rendering */
194 	fprintf(stderr, "Rendering...\n");
195 
196 	struct timeval begin, end;
197 
198 	gettimeofday(&begin, NULL);
199 	int ntiles = RenderTiles(pbuffer, viewer, layer, argv[1], minlon, minlat, maxlon, maxlat, minzoom, maxzoom, pnglevel);
200 	gettimeofday(&end, NULL);
201 
202 	float dt = (float)(end.tv_sec - begin.tv_sec) + (float)(end.tv_usec - begin.tv_usec)/1000000.0f;
203 
204 	fprintf(stderr, "%.2f seconds, %d tiles: %.2f tiles/sec\n", dt, ntiles, (float)ntiles/dt);
205 
206 	return 0;
207 }
208 
main(int argc,char ** argv)209 int main(int argc, char** argv) {
210 	try {
211 		return real_main(argc, argv);
212 	} catch (std::exception &e) {
213 		fprintf(stderr, "Exception: %s\n", e.what());
214 	} catch (...) {
215 		fprintf(stderr, "Unknown exception\n");
216 	}
217 
218 	return 1;
219 }
220