now with cgi-mode and lots of other improvements.
authorMichael 'PoempelFox' Meier <git@mulder.franken.de>
Fri, 31 Jul 2015 14:03:57 +0000 (16:03 +0200)
committerMichael 'PoempelFox' Meier <git@mulder.franken.de>
Fri, 31 Jul 2015 14:03:57 +0000 (16:03 +0200)
scripts/osmbuildings-json-generator.pl

index 0d6c9d86f3eb0d28ed91421d7c10e95c86061b89..16ad1c373857b29fc74ec8a02b805d3a3e3812fb 100755 (executable)
@@ -51,16 +51,55 @@ sub fetchlastofhr($$@) {
   return $res;
 }
 
+# Helper functions for the CGI variant. Taken from the openstreetmap-Wiki.
+use Math::Trig;
+sub Project {
+  my ($X, $Y, $Zoom) = @_;
+  my $Unit = 1 / (2 ** $Zoom);
+  my $relY1 = $Y * $Unit;
+  my $relY2 = $relY1 + $Unit;
+  # note: $LimitY = ProjectF(degrees(atan(sinh(pi)))) = log(sinh(pi)+cosh(pi)) = pi
+  # note: degrees(atan(sinh(pi))) = 85.051128..
+  #my $LimitY = ProjectF(85.0511);
+  # so stay simple and more accurate
+  my $LimitY = pi;
+  my $RangeY = 2 * $LimitY;
+  $relY1 = $LimitY - $RangeY * $relY1;
+  $relY2 = $LimitY - $RangeY * $relY2;
+  my $Lat1 = ProjectMercToLat($relY1);
+  my $Lat2 = ProjectMercToLat($relY2);
+  $Unit = 360 / (2 ** $Zoom);
+  my $Long1 = -180 + $X * $Unit;
+  return ($Lat2, $Long1, $Lat1, $Long1 + $Unit); # S,W,N,E
+}
+sub ProjectMercToLat($){
+  my $MercY = shift;
+  return rad2deg(atan(sinh($MercY)));
+}
+sub ProjectF
+{
+  my $Lat = shift;
+  $Lat = deg2rad($Lat);
+  my $Y = log(tan($Lat) + sec($Lat));
+  return $Y;
+}
+
 # ----------------------------------------------------------------------------
 # main()
 # ----------------------------------------------------------------------------
 
+$iscgi = 0;
+
 # Parse commandline
 foreach $a (@ARGV) {
   if ($a eq '-q') {
     $verblev--;
   } elsif ($a eq '-v') {
     $verblev++;
+  } elsif ($a eq '--cgi') {
+    $iscgi = 1;
   } else {
     print("Unknown parameter: $a\n");
     print("Syntax: $0 [-q] [-v]\n");
@@ -69,6 +108,22 @@ foreach $a (@ARGV) {
   }
 }
 
+if (defined($ENV{'REQUEST_URI'})) {
+  $iscgi = 1;
+  print("Content-type: application/json\n\n");
+  unless ($ENV{'REQUEST_URI'} =~ m!(\d+)/(\d+)/(\d+)\.json$!) {
+    print("{\n\"_comment\": \"Sorry, query parameters not understood.\"\n}\n");
+    exit(0);
+  }
+  my $cgiz = int($1); my $cgix = int($2); my $cgiy = int($3);
+  #print("DEBUG: z=$cgiz x=$cgix y=$cgiy\n");
+  if ($cgiz < 15) {
+    print("{\n\"_comment\": \"No data will be returned at this zoom level.\"\n}\n");
+  }
+  ($y1, $x1, $y2, $x2) = Project($cgix, $cgiy, $cgiz);
+  #print("DEBUG: mapped to ($x1 $y1) ($x2 $y2)\n");
+}
+
 unless ($dbh = DBI->connect("dbi:Pg:dbname=$dbname","","")) {
   print(STDERR "Failed to open database. Please try again later.\n"); exit(1);
 }
@@ -78,43 +133,73 @@ $ty1 = $dbh->selectrow_array("select ST_Y(ST_transform(ST_GeomFromText('POINT($x
 $tx2 = $dbh->selectrow_array("select ST_X(ST_transform(ST_GeomFromText('POINT($x2 $y2)', 4326), 900913))");
 $ty2 = $dbh->selectrow_array("select ST_Y(ST_transform(ST_GeomFromText('POINT($x2 $y2)', 4326), 900913))");
 my $cntr = 0;
-print("// Note: this file has been autogenerated by $0\n");
-print("var FAUGeoJSON = {\n");
+if ($iscgi == 0) {
+  print("// Note: this file has been autogenerated by $0\n");
+  print("var FAUGeoJSON = {\n");
+} else {
+  print("{\n");
+}
 print("  \"type\": \"FeatureCollection\",\n");
 print("  \"features\": [\n");
-foreach $table ('planet_osm_line', 'planet_osm_polygon') {
+foreach $xtable ('planet_osm_polygon1', 'planet_osm_polygon2', 'planet_osm_line') { # 'planet_osm_polygon', 'planet_osm_line'
+  my $querypart1 = " ((tags->'building:part') is not null)";
+  my $table = 'planet_osm_polygon'; 
+  if ($xtable eq 'planet_osm_polygon1') {
+    $querypart1 = " (building is not null)";
+  } elsif ($xtable eq 'planet_osm_line') {
+    $table = $xtable;
+  }
+  # Note: && in this case is the postgis operator for 'bounding box overlaps'.
   my $sth = $dbh->prepare("select osm_id, way, building, 'tower:type', name, tags"
-                        . " from $table where"
-                        . "   (building is not null or (tags->'building:part') is not null)"
-                        . "   and way && ST_MakeEnvelope($tx1, $ty1, $tx2, $ty2)");
+                        . " from $table where $querypart1"
+                        . "   and (way && ST_MakeEnvelope($tx1, $ty1, $tx2, $ty2))");
   unless ($sth->execute()) {
-    print(STDERR "Sorry, database query exploded.!\n");
+    print(STDERR "Sorry, database query blew up.!\n");
     exit(1);
   }
   my $sth2 = $dbh->prepare("select ST_X(points) as x, ST_Y(points) as y"
                          . " from (select ST_astext((ST_dumppoints(ST_transform(?, 4326))).geom) as points) as points");
   while ($row = $sth->fetchrow_hashref()) {
+    if ($xtable eq 'planet_osm_polygon1') {
+      my $hassubparts = $dbh->selectrow_array("select count(*)"
+                                          . " from planet_osm_line"
+                                          . " where ((tags->'building:part') is not null)"
+                                          . " and (ST_Covers(" . $dbh->quote($row->{'way'}) . ", way) = true)");
+      # The subparts usually cover the whole building, so we do NOT draw the
+      # whole building but only the subparts.
+      if ($hassubparts > 0) { next; }
+    }
     my $hstoredec;
     if (defined($row->{'tags'})) {
       $hstoredec = Pg::hstore::decode($row->{'tags'});
     }
-    my $height = fetchlastofhr(1, $hstoredec, 'levels', 'building:levels');
-    $height = $height * 2.5;
-    $height = fetchlastofhr($height, $hstoredec, 'height', 'building:height');
-    $height = sprintf("%.1f", $height);
-    my $minheight = fetchlastofhr(0, $hstoredec, 'min_levels', 'building:min_levels');
-    $minheight = $minheight * 2.5;
-    $minheight = fetchlastofhr($minheight, $hstoredec, 'min_height', 'building:min_height');
-    $minheight = sprintf("%.1f", $minheight);
+    my $levels = fetchlastofhr(1, $hstoredec, 'levels', 'building:levels');
+    my $height = fetchlastofhr($height, $hstoredec, 'height', 'building:height');
+    unless ($height =~ m/^[0-9.]+$/) { undef($height); }
+    my $minlevel = fetchlastofhr(0, $hstoredec,
+                                 'min_levels', 'building:min_levels', 'min_level', 'building:min_level');
+    my $minheight = fetchlastofhr($minheight, $hstoredec, 'min_height', 'building:min_height');
+    unless ($minheight =~ m/^[0-9.]+$/) { undef($minheight); }
     my $wallcolor = fetchlastofhr(undef, $hstoredec, 'building:color', 'building:colour');
     my $roofcolor = fetchlastofhr(undef, $hstoredec,
                                   'roof:color', 'roof:colour', 'building:roof:color', 'building:roof:colour');
     my $roofshape = fetchlastofhr(undef, $hstoredec, 'roof:shape', 'building:roof:shape');
     my $roofheight = fetchlastofhr(undef, $hstoredec, 'roof:height', 'building:roof:height');
+    if ($cntr != 0) {
+      print(",\n");
+    }
     $cntr++;
     print("  {\n");
     print("    \"type\": \"Feature\",\n");
-    print("    \"id\": $cntr,\n");
+    if (defined($row->{'osm_id'})) {
+      print("    \"id\": " . $row->{'osm_id'} . ",\n");
+    } else {
+      print("    \"id\": $cntr,\n");
+    }
+    if (defined($row->{'name'})) {
+      $row->{'name'} =~ s/"//g;
+      print("    \"name\": \"" . $row->{'name'} . "\",\n");
+    }
     print("    \"geometry\": {\n");
     print("      \"type\": \"Polygon\",\n");
     print("      \"coordinates\": [[\n");
@@ -123,34 +208,58 @@ foreach $table ('planet_osm_line', 'planet_osm_polygon') {
       exit(1);
     }
     my ($onex, $oney);
+    my $firstcoord = 1;
     while (($onex, $oney) = $sth2->fetchrow_array()) {
-      print("        [ $onex, $oney ],\n");
+      if ($firstcoord == 1) {
+        $firstcoord = 0;
+      } else {
+        print(",\n");
+      }
+      printf("        [ %.5f, %.5f ]", $onex, $oney);
     }
+    print("\n");
     print("      ]]\n");
     print("    },\n");
     print("    \"properties\": {\n");
-    print("       \"height\": $height,\n");
-    print("       \"min_height\": $minheight,\n");
+    if (defined($height)) {
+      print("       \"height\": $height,\n");
+    }
+    if (defined($min_height)) {
+      print("       \"min_height\": $minheight,\n");
+    }
+    if (defined($levels)) {
+      print("       \"levels\": $levels,\n");
+    }
+    if (defined($minlevel)) {
+      print("       \"minLevel\": $minlevel,\n");
+    }
     if (defined($wallcolor)) {
       print("       \"wallColor\": \"$wallcolor\",\n");
     }
     if (defined($roofcolor)) {
+      $roofcolor =~ s/"//g;
       print("       \"roofColor\": \"$roofcolor\",\n");
     }
     if (defined($roofshape)) {
+      $roofshape =~ s/"//g;
       print("       \"roofShape\": \"$roofshape\",\n");
     }
     if (defined($roofheight)) {
       $roofheight = sprintf("%.1f", $roofheight);
       print("       \"roofHeight\": \"$roofheight\",\n");
     }
+    print("       \"dummy\": \"void\"\n");
     print("    }\n");
-    print("  },\n");
+    print("  }");
   }
   $sth2->finish();
   undef($sth2);
   $sth->finish();
   undef($sth);
 }
-print("  ]\n");
-print("};\n");
+print("\n  ]\n");
+if ($iscgi == 0) {
+  print("};\n");
+} else {
+  print("}\n");
+}
This page took 0.151911 seconds and 4 git commands to generate.