fix my double-escape-FAIL
[osmrrze.git] / scripts / osmbuildings-json-generator.pl
CommitLineData
d58b8e00
MPM
1#!/usr/bin/perl -w
2
3# Our database
4$dbname = 'osm';
5
6# Default verbosity level? Can be increased with -v, decreased with -q
7$verblev = 0;
8
9# The area which we cover
10$x1 = 11.01;
11$y1 = 49.56;
12$x2 = 11.04;
13$y2 = 49.58;
14
15# There should be no need to touch anything below this line.
16# ---------------------------------------------------------------------------
17
18use DBI;
19use POSIX qw(strftime mktime);
20use Pg::hstore;
6b3575c2 21use JSON;
d58b8e00
MPM
22
23# Par. 0: Level on which this gets printed
24# Par. 1: Text
25sub printlev($$) {
26 unless (defined($printlev_atbeginofline)) { $printlev_atbeginofline = 1; }
27 if ($verblev >= $_[0]) {
28 if ($printlev_atbeginofline) {
29 print(strftime("[%Y%m%d-%H%M%S] ", localtime(time())));
30 }
31 print($_[1]);
32 if ($_[1] =~ m/\n$/) {
33 $printlev_atbeginofline = 1;
34 } else {
35 $printlev_atbeginofline = 0;
36 }
37 }
38}
39
40# Par. 0: default value to return if nothing is defined
41# Par. 1: the hashref containing our variables.
42# Par. 2: variables to query. The LAST one that exists will be returned.
43sub fetchlastofhr($$@) {
44 my $res; my $hr; my @vns;
45 ($res, $hr, @vns) = @_;
46 for (my $i = 0; $i < @vns; $i++) {
47 my $vn = $vns[$i];
48 if (defined($hr->{$vn})) {
49 $res = $hr->{$vn};
50 }
51 }
52 return $res;
53}
54
9de6d037
MPM
55# Helper functions for the CGI variant. Taken from the openstreetmap-Wiki.
56use Math::Trig;
57sub Project {
58 my ($X, $Y, $Zoom) = @_;
59 my $Unit = 1 / (2 ** $Zoom);
60 my $relY1 = $Y * $Unit;
61 my $relY2 = $relY1 + $Unit;
62
63 # note: $LimitY = ProjectF(degrees(atan(sinh(pi)))) = log(sinh(pi)+cosh(pi)) = pi
64 # note: degrees(atan(sinh(pi))) = 85.051128..
65 #my $LimitY = ProjectF(85.0511);
66
67 # so stay simple and more accurate
68 my $LimitY = pi;
69 my $RangeY = 2 * $LimitY;
70 $relY1 = $LimitY - $RangeY * $relY1;
71 $relY2 = $LimitY - $RangeY * $relY2;
72 my $Lat1 = ProjectMercToLat($relY1);
73 my $Lat2 = ProjectMercToLat($relY2);
74 $Unit = 360 / (2 ** $Zoom);
75 my $Long1 = -180 + $X * $Unit;
76 return ($Lat2, $Long1, $Lat1, $Long1 + $Unit); # S,W,N,E
77}
78sub ProjectMercToLat($){
79 my $MercY = shift;
80 return rad2deg(atan(sinh($MercY)));
81}
82sub ProjectF
83{
84 my $Lat = shift;
85 $Lat = deg2rad($Lat);
86 my $Y = log(tan($Lat) + sec($Lat));
87 return $Y;
88}
89
e79ebcd6
MPM
90# print that drops whitespace when not in verbose mode.
91sub printwows($) {
92 my $res = $_[0];
93 if ($verblev > 0) {
94 print($res); return;
95 }
96 $res =~ s/^\s+//g;
97 $res =~ s/"\s*:\s*/":/g;
98 $res =~ s/[\r\n]//g;
99 print($res);
100}
101
d58b8e00
MPM
102# ----------------------------------------------------------------------------
103# main()
104# ----------------------------------------------------------------------------
105
9de6d037
MPM
106$iscgi = 0;
107
d58b8e00
MPM
108# Parse commandline
109foreach $a (@ARGV) {
110 if ($a eq '-q') {
111 $verblev--;
112 } elsif ($a eq '-v') {
113 $verblev++;
9de6d037
MPM
114 } elsif ($a eq '--cgi') {
115 $iscgi = 1;
d58b8e00
MPM
116 } else {
117 print("Unknown parameter: $a\n");
118 print("Syntax: $0 [-q] [-v]\n");
119 print(" -q decreases verbosity, -v increases verbosity.\n");
120 exit(1);
121 }
122}
123
9de6d037
MPM
124if (defined($ENV{'REQUEST_URI'})) {
125 $iscgi = 1;
6b3575c2 126 print("Content-type: application/json; charset=utf-8\n");
e79ebcd6
MPM
127 print("Access-Control-Allow-Origin: *\n");
128 print("Cache-Control: public, max-age=3600\n");
129 print("\n");
9de6d037
MPM
130 unless ($ENV{'REQUEST_URI'} =~ m!(\d+)/(\d+)/(\d+)\.json$!) {
131 print("{\n\"_comment\": \"Sorry, query parameters not understood.\"\n}\n");
132 exit(0);
133 }
134 my $cgiz = int($1); my $cgix = int($2); my $cgiy = int($3);
135 #print("DEBUG: z=$cgiz x=$cgix y=$cgiy\n");
136 if ($cgiz < 15) {
137 print("{\n\"_comment\": \"No data will be returned at this zoom level.\"\n}\n");
138 }
139 ($y1, $x1, $y2, $x2) = Project($cgix, $cgiy, $cgiz);
140 #print("DEBUG: mapped to ($x1 $y1) ($x2 $y2)\n");
141}
142
d58b8e00
MPM
143unless ($dbh = DBI->connect("dbi:Pg:dbname=$dbname","","")) {
144 print(STDERR "Failed to open database. Please try again later.\n"); exit(1);
145}
146
147$tx1 = $dbh->selectrow_array("select ST_X(ST_transform(ST_GeomFromText('POINT($x1 $y1)', 4326), 900913))");
148$ty1 = $dbh->selectrow_array("select ST_Y(ST_transform(ST_GeomFromText('POINT($x1 $y1)', 4326), 900913))");
149$tx2 = $dbh->selectrow_array("select ST_X(ST_transform(ST_GeomFromText('POINT($x2 $y2)', 4326), 900913))");
150$ty2 = $dbh->selectrow_array("select ST_Y(ST_transform(ST_GeomFromText('POINT($x2 $y2)', 4326), 900913))");
151my $cntr = 0;
9de6d037
MPM
152if ($iscgi == 0) {
153 print("// Note: this file has been autogenerated by $0\n");
e79ebcd6 154 printwows("var FAUGeoJSON = {\n");
9de6d037 155} else {
e79ebcd6 156 printwows("{\n");
9de6d037 157}
e79ebcd6
MPM
158printwows(" \"type\": \"FeatureCollection\",\n");
159printwows(" \"features\": [\n");
9de6d037
MPM
160foreach $xtable ('planet_osm_polygon1', 'planet_osm_polygon2', 'planet_osm_line') { # 'planet_osm_polygon', 'planet_osm_line'
161 my $querypart1 = " ((tags->'building:part') is not null)";
162 my $table = 'planet_osm_polygon';
163 if ($xtable eq 'planet_osm_polygon1') {
164 $querypart1 = " (building is not null)";
165 } elsif ($xtable eq 'planet_osm_line') {
166 $table = $xtable;
167 }
168 # Note: && in this case is the postgis operator for 'bounding box overlaps'.
d58b8e00 169 my $sth = $dbh->prepare("select osm_id, way, building, 'tower:type', name, tags"
9de6d037
MPM
170 . " from $table where $querypart1"
171 . " and (way && ST_MakeEnvelope($tx1, $ty1, $tx2, $ty2))");
d58b8e00 172 unless ($sth->execute()) {
9de6d037 173 print(STDERR "Sorry, database query blew up.!\n");
d58b8e00
MPM
174 exit(1);
175 }
176 my $sth2 = $dbh->prepare("select ST_X(points) as x, ST_Y(points) as y"
177 . " from (select ST_astext((ST_dumppoints(ST_transform(?, 4326))).geom) as points) as points");
178 while ($row = $sth->fetchrow_hashref()) {
9de6d037
MPM
179 if ($xtable eq 'planet_osm_polygon1') {
180 my $hassubparts = $dbh->selectrow_array("select count(*)"
181 . " from planet_osm_line"
182 . " where ((tags->'building:part') is not null)"
183 . " and (ST_Covers(" . $dbh->quote($row->{'way'}) . ", way) = true)");
184 # The subparts usually cover the whole building, so we do NOT draw the
185 # whole building but only the subparts.
186 if ($hassubparts > 0) { next; }
187 }
d58b8e00
MPM
188 my $hstoredec;
189 if (defined($row->{'tags'})) {
190 $hstoredec = Pg::hstore::decode($row->{'tags'});
191 }
9de6d037 192 my $levels = fetchlastofhr(1, $hstoredec, 'levels', 'building:levels');
2c78883c
MPM
193 my $height = fetchlastofhr(undef, $hstoredec, 'height', 'building:height');
194 unless ((defined($height)) && ($height =~ m/^[0-9.]+$/)) {
195 undef($height);
196 }
9de6d037
MPM
197 my $minlevel = fetchlastofhr(0, $hstoredec,
198 'min_levels', 'building:min_levels', 'min_level', 'building:min_level');
2c78883c
MPM
199 my $minheight = fetchlastofhr(undef, $hstoredec, 'min_height', 'building:min_height');
200 unless ((defined($minheight)) && ($minheight =~ m/^[0-9.]+$/)) {
201 undef($minheight);
202 }
6b3575c2
MPM
203 my $shape = fetchlastofhr(undef, $hstoredec, 'building:shape');
204 my $material = fetchlastofhr(undef, $hstoredec,
205 'building:material', 'building:facade:material', 'building:cladding');
d58b8e00
MPM
206 my $wallcolor = fetchlastofhr(undef, $hstoredec, 'building:color', 'building:colour');
207 my $roofcolor = fetchlastofhr(undef, $hstoredec,
208 'roof:color', 'roof:colour', 'building:roof:color', 'building:roof:colour');
209 my $roofshape = fetchlastofhr(undef, $hstoredec, 'roof:shape', 'building:roof:shape');
210 my $roofheight = fetchlastofhr(undef, $hstoredec, 'roof:height', 'building:roof:height');
e79ebcd6 211 my $roofmaterial = fetchlastofhr(undef, $hstoredec, 'roof:material', 'building:roof:material');
9de6d037 212 if ($cntr != 0) {
e79ebcd6 213 printwows(",\n");
9de6d037 214 }
d58b8e00 215 $cntr++;
e79ebcd6
MPM
216 printwows(" {\n");
217 printwows(" \"type\": \"Feature\",\n");
9de6d037 218 if (defined($row->{'osm_id'})) {
e79ebcd6 219 printwows(" \"id\": " . $row->{'osm_id'} . ",\n");
9de6d037 220 } else {
e79ebcd6 221 printwows(" \"id\": $cntr,\n");
9de6d037
MPM
222 }
223 if (defined($row->{'name'})) {
224 $row->{'name'} =~ s/"//g;
e79ebcd6 225 printwows(" \"name\": \"" . $row->{'name'} . "\",\n");
9de6d037 226 }
e79ebcd6
MPM
227 printwows(" \"geometry\": {\n");
228 printwows(" \"type\": \"Polygon\",\n");
229 printwows(" \"coordinates\": [[\n");
d58b8e00
MPM
230 unless ($sth2->execute($row->{'way'})) {
231 print(STDERR "Sorry, decoding the way data exploded.\n");
232 exit(1);
233 }
234 my ($onex, $oney);
9de6d037 235 my $firstcoord = 1;
d58b8e00 236 while (($onex, $oney) = $sth2->fetchrow_array()) {
9de6d037
MPM
237 if ($firstcoord == 1) {
238 $firstcoord = 0;
239 } else {
e79ebcd6 240 printwows(",\n");
9de6d037 241 }
e79ebcd6 242 printwows(sprintf(" [ %.5f, %.5f ]", $onex, $oney));
d58b8e00 243 }
e79ebcd6
MPM
244 printwows("\n");
245 printwows(" ]]\n");
246 printwows(" },\n");
247 printwows(" \"properties\": {\n");
9de6d037 248 if (defined($height)) {
e79ebcd6 249 printwows(" \"height\": $height,\n");
9de6d037 250 }
e79ebcd6
MPM
251 if (defined($minheight)) {
252 printwows(" \"minHeight\": $minheight,\n");
9de6d037
MPM
253 }
254 if (defined($levels)) {
e79ebcd6 255 printwows(" \"levels\": $levels,\n");
9de6d037
MPM
256 }
257 if (defined($minlevel)) {
e79ebcd6 258 printwows(" \"minLevel\": $minlevel,\n");
9de6d037 259 }
6b3575c2
MPM
260 if (defined($shape)) {
261 $shape = JSON->new->allow_nonref->encode($shape);
262 printwows(" \"shape\": $shape,\n");
263 }
264 if (defined($material)) {
265 $material = JSON->new->allow_nonref->encode($material);
266 printwows(" \"material\": $material,\n");
267 }
d58b8e00 268 if (defined($wallcolor)) {
6b3575c2 269 $wallcolor = JSON->new->allow_nonref->encode($wallcolor);
2c78883c 270 printwows(" \"wallColor\": $wallcolor,\n");
d58b8e00
MPM
271 }
272 if (defined($roofcolor)) {
6b3575c2 273 $roofcolor = JSON->new->allow_nonref->encode($roofcolor);
2c78883c 274 printwows(" \"roofColor\": $roofcolor,\n");
d58b8e00
MPM
275 }
276 if (defined($roofshape)) {
e79ebcd6 277 if ($roofshape eq 'pyramidal') { $roofshape = 'pyramid'; }
6b3575c2 278 $roofshape = JSON->new->allow_nonref->encode($roofshape);
2c78883c 279 printwows(" \"roofShape\": $roofshape,\n");
e79ebcd6
MPM
280 }
281 if (defined($roofmaterial)) {
6b3575c2 282 $roofmaterial = JSON->new->allow_nonref->encode($roofmaterial);
2c78883c 283 printwows(" \"roofMaterial\": $roofmaterial,\n");
d58b8e00
MPM
284 }
285 if (defined($roofheight)) {
286 $roofheight = sprintf("%.1f", $roofheight);
e79ebcd6 287 printwows(" \"roofHeight\": \"$roofheight\",\n");
d58b8e00 288 }
e79ebcd6
MPM
289 # this line is completely useless, but we need it to guarantee JSON has
290 # no trailing comma. osmbuildings will accept JSON with trailing commas
291 # just fine when you give it static, but NOT when requesting it dynamically.
292 printwows(" \"K9\": \"\"\n");
293 printwows(" }\n");
294 printwows(" }");
d58b8e00
MPM
295 }
296 $sth2->finish();
297 undef($sth2);
298 $sth->finish();
299 undef($sth);
300}
e79ebcd6 301printwows("\n ]\n");
9de6d037
MPM
302if ($iscgi == 0) {
303 print("};\n");
304} else {
305 print("}\n");
306}
This page took 0.158103 seconds and 4 git commands to generate.