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