Commit | Line | Data |
---|---|---|
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 | ||
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 | ||
9de6d037 MPM |
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 | ||
d58b8e00 MPM |
89 | # ---------------------------------------------------------------------------- |
90 | # main() | |
91 | # ---------------------------------------------------------------------------- | |
92 | ||
9de6d037 MPM |
93 | $iscgi = 0; |
94 | ||
d58b8e00 MPM |
95 | # Parse commandline |
96 | foreach $a (@ARGV) { | |
97 | if ($a eq '-q') { | |
98 | $verblev--; | |
99 | } elsif ($a eq '-v') { | |
100 | $verblev++; | |
9de6d037 MPM |
101 | } elsif ($a eq '--cgi') { |
102 | $iscgi = 1; | |
d58b8e00 MPM |
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 | ||
9de6d037 MPM |
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 | ||
d58b8e00 MPM |
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; | |
9de6d037 MPM |
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 | } | |
d58b8e00 MPM |
142 | print(" \"type\": \"FeatureCollection\",\n"); |
143 | print(" \"features\": [\n"); | |
9de6d037 MPM |
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'. | |
d58b8e00 | 153 | my $sth = $dbh->prepare("select osm_id, way, building, 'tower:type', name, tags" |
9de6d037 MPM |
154 | . " from $table where $querypart1" |
155 | . " and (way && ST_MakeEnvelope($tx1, $ty1, $tx2, $ty2))"); | |
d58b8e00 | 156 | unless ($sth->execute()) { |
9de6d037 | 157 | print(STDERR "Sorry, database query blew up.!\n"); |
d58b8e00 MPM |
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()) { | |
9de6d037 MPM |
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 | } | |
d58b8e00 MPM |
172 | my $hstoredec; |
173 | if (defined($row->{'tags'})) { | |
174 | $hstoredec = Pg::hstore::decode($row->{'tags'}); | |
175 | } | |
9de6d037 MPM |
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); } | |
d58b8e00 MPM |
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'); | |
9de6d037 MPM |
188 | if ($cntr != 0) { |
189 | print(",\n"); | |
190 | } | |
d58b8e00 MPM |
191 | $cntr++; |
192 | print(" {\n"); | |
193 | print(" \"type\": \"Feature\",\n"); | |
9de6d037 MPM |
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 | } | |
d58b8e00 MPM |
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); | |
9de6d037 | 211 | my $firstcoord = 1; |
d58b8e00 | 212 | while (($onex, $oney) = $sth2->fetchrow_array()) { |
9de6d037 MPM |
213 | if ($firstcoord == 1) { |
214 | $firstcoord = 0; | |
215 | } else { | |
216 | print(",\n"); | |
217 | } | |
218 | printf(" [ %.5f, %.5f ]", $onex, $oney); | |
d58b8e00 | 219 | } |
9de6d037 | 220 | print("\n"); |
d58b8e00 MPM |
221 | print(" ]]\n"); |
222 | print(" },\n"); | |
223 | print(" \"properties\": {\n"); | |
9de6d037 MPM |
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 | } | |
d58b8e00 MPM |
236 | if (defined($wallcolor)) { |
237 | print(" \"wallColor\": \"$wallcolor\",\n"); | |
238 | } | |
239 | if (defined($roofcolor)) { | |
9de6d037 | 240 | $roofcolor =~ s/"//g; |
d58b8e00 MPM |
241 | print(" \"roofColor\": \"$roofcolor\",\n"); |
242 | } | |
243 | if (defined($roofshape)) { | |
9de6d037 | 244 | $roofshape =~ s/"//g; |
d58b8e00 MPM |
245 | print(" \"roofShape\": \"$roofshape\",\n"); |
246 | } | |
247 | if (defined($roofheight)) { | |
248 | $roofheight = sprintf("%.1f", $roofheight); | |
249 | print(" \"roofHeight\": \"$roofheight\",\n"); | |
250 | } | |
9de6d037 | 251 | print(" \"dummy\": \"void\"\n"); |
d58b8e00 | 252 | print(" }\n"); |
9de6d037 | 253 | print(" }"); |
d58b8e00 MPM |
254 | } |
255 | $sth2->finish(); | |
256 | undef($sth2); | |
257 | $sth->finish(); | |
258 | undef($sth); | |
259 | } | |
9de6d037 MPM |
260 | print("\n ]\n"); |
261 | if ($iscgi == 0) { | |
262 | print("};\n"); | |
263 | } else { | |
264 | print("}\n"); | |
265 | } |