add explicit cast, else postgresql 9.5 will break (9.3 works fine)
[osmrrze.git] / scripts / osmbuildings-json-generator.pl
index 502f91b3efdebf1c71570bc0b075a0a8ab9822b0..3a39357550c2fe6aadb9e8acbf1ae7b01af0cd65 100755 (executable)
@@ -52,19 +52,15 @@ sub fetchlastofhr($$@) {
   return $res;
 }
 
-# Helper functions for the CGI variant. Taken from the openstreetmap-Wiki.
+# Helper functions for the CGI variant. Taken from the openstreetmap-Wiki,
+# which in turn is almost a direct copy of a GPL'd script included in (amongst
+# others) the mapnik sourcecode. -> GPL code
 use Math::Trig;
-sub Project {
+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;
@@ -75,17 +71,14 @@ sub Project {
   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 ProjectMercToLat($) {
+  return rad2deg(atan(sinh($_[0])));
 }
-sub ProjectF
-{
-  my $Lat = shift;
-  $Lat = deg2rad($Lat);
-  my $Y = log(tan($Lat) + sec($Lat));
-  return $Y;
+sub ProjectF($) {
+  $Lat = deg2rad($_[0]);
+  return (log(tan($Lat) + sec($Lat)));
 }
+# ----- end of copied helper functions -----
 
 # print that drops whitespace when not in verbose mode.
 sub printwows($) {
@@ -148,6 +141,16 @@ $tx1 = $dbh->selectrow_array("select ST_X(ST_transform(ST_GeomFromText('POINT($x
 $ty1 = $dbh->selectrow_array("select ST_Y(ST_transform(ST_GeomFromText('POINT($x1 $y1)', 4326), 900913))");
 $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))");
+# Why do crappy perl libraries always insist on using an "Object Oriented" interface
+# for things where this does not make any sense at all?
+my $crappyjsonoo = JSON->new()->latin1(); # Note: has to be latin1 instead of utf8
+# for now, because the strings we get from the DB are WTF8, but are not marked
+# as such. Therefore, we must not tell the JSON module to do utf8(), because
+# otherwise it would encode the WTF8 twice.
+# This will change with DBI::Pg modules >3 where UTF8 is properly handled.
+if ($verblev > 0) {
+  $crappyjsonoo = $crappyjsonoo->pretty();
+}
 my $cntr = 0;
 if ($iscgi == 0) {
   print("// Note: this file has been autogenerated by $0\n");
@@ -174,13 +177,14 @@ foreach $xtable ('planet_osm_polygon1', 'planet_osm_polygon2', 'planet_osm_line'
     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");
+                         . " from (select ST_astext((ST_dumppoints(ST_transform(geometry(?), 4326))).geom) as points) as points");
+  my $sth4 = $dbh->prepare("select (ST_dumprings(?)).geom as rings");
   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)");
+                                            . " 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; }
@@ -190,12 +194,16 @@ foreach $xtable ('planet_osm_polygon1', 'planet_osm_polygon2', 'planet_osm_line'
       $hstoredec = Pg::hstore::decode($row->{'tags'});
     }
     my $levels = fetchlastofhr(1, $hstoredec, 'levels', 'building:levels');
-    my $height = fetchlastofhr($height, $hstoredec, 'height', 'building:height');
-    unless ($height =~ m/^[0-9.]+$/) { undef($height); }
+    my $height = fetchlastofhr(undef, $hstoredec, 'height', 'building:height');
+    unless ((defined($height)) && ($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 $minheight = fetchlastofhr(undef, $hstoredec, 'min_height', 'building:min_height');
+    unless ((defined($minheight)) && ($minheight =~ m/^[0-9.]+$/)) {
+      undef($minheight);
+    }
     my $shape = fetchlastofhr(undef, $hstoredec, 'building:shape');
     my $material = fetchlastofhr(undef, $hstoredec,
                                  'building:material', 'building:facade:material', 'building:cladding');
@@ -204,93 +212,90 @@ foreach $xtable ('planet_osm_polygon1', 'planet_osm_polygon2', 'planet_osm_line'
                                   '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');
+    unless ((defined($roofheight)) && ($roofheight =~ m/^[0-9.]+$/)) {
+      undef($roofheight);
+    }
     my $roofmaterial = fetchlastofhr(undef, $hstoredec, 'roof:material', 'building:roof:material');
     if ($cntr != 0) {
       printwows(",\n");
     }
     $cntr++;
-    printwows("  {\n");
-    printwows("    \"type\": \"Feature\",\n");
-    if (defined($row->{'osm_id'})) {
-      printwows("    \"id\": " . $row->{'osm_id'} . ",\n");
-    } else {
-      printwows("    \"id\": $cntr,\n");
-    }
+    my $jso = { "type" => "Feature" };
+    $jso->{'id'} = (defined($row->{'osm_id'}) ? $row->{'osm_id'} : $cntr);
     if (defined($row->{'name'})) {
-      $row->{'name'} =~ s/"//g;
-      printwows("    \"name\": \"" . $row->{'name'} . "\",\n");
-    }
-    printwows("    \"geometry\": {\n");
-    printwows("      \"type\": \"Polygon\",\n");
-    printwows("      \"coordinates\": [[\n");
-    unless ($sth2->execute($row->{'way'})) {
-      print(STDERR "Sorry, decoding the way data exploded.\n");
-      exit(1);
+      $jso->{'name'} = $row->{'name'};
     }
-    my ($onex, $oney);
-    my $firstcoord = 1;
-    while (($onex, $oney) = $sth2->fetchrow_array()) {
-      if ($firstcoord == 1) {
-        $firstcoord = 0;
+    my @waystodecode = ();
+    {
+      local $sth4->{'PrintError'};  # We're fully aware that the execute can
+      local $sth4->{'PrintWarn'};   # fail, no need to spam about it.
+      if ($sth4->execute($row->{'way'})) {
+        while ((my $nxtway) = $sth4->fetchrow_array()) {
+          push(@waystodecode, $nxtway);
+        }
       } else {
-        printwows(",\n");
+        push(@waystodecode, $row->{'way'});
+      }
+    }
+    my @geomarr2 = ();
+    foreach my $nxtway (@waystodecode) {
+      unless ($sth2->execute($nxtway)) {
+        print(STDERR "Sorry, decoding the way data exploded.\n");
+        exit(1);
+      }
+      my ($onex, $oney);
+      my @geomarr1 = ();
+      while (($onex, $oney) = $sth2->fetchrow_array()) {
+        my @onecoord = ( sprintf("%.5f", $onex), sprintf("%.5f", $oney));
+        push (@geomarr1, \@onecoord);
       }
-      printwows(sprintf("        [ %.5f, %.5f ]", $onex, $oney));
+      push(@geomarr2, \@geomarr1);
+      $sth2->finish();
     }
-    printwows("\n");
-    printwows("      ]]\n");
-    printwows("    },\n");
-    printwows("    \"properties\": {\n");
+    my $geomhsh = { "type" => "Polygon",
+                    "coordinates" => \@geomarr2 };
+    $jso->{'geometry'} = $geomhsh;
+    my $prophsh = { };
     if (defined($height)) {
-      printwows("       \"height\": $height,\n");
+      $prophsh->{'height'} = $height;
     }
     if (defined($minheight)) {
-      printwows("       \"minHeight\": $minheight,\n");
+      $prophsh->{'minHeight'} = $minheight;
     }
     if (defined($levels)) {
-      printwows("       \"levels\": $levels,\n");
+      $prophsh->{'levels'} = $levels;
     }
     if (defined($minlevel)) {
-      printwows("       \"minLevel\": $minlevel,\n");
+      $prophsh->{'minLevel'} = $minlevel;
     }
     if (defined($shape)) {
-      $shape = JSON->new->allow_nonref->encode($shape);
-      printwows("       \"shape\": $shape,\n");
+      $prophsh->{'shape'} = $shape;
     }
     if (defined($material)) {
-      $material = JSON->new->allow_nonref->encode($material);
-      printwows("       \"material\": $material,\n");
+      $prophsh->{'material'} = $material;
     }
     if (defined($wallcolor)) {
-      $wallcolor = JSON->new->allow_nonref->encode($wallcolor);
-      printwows("       \"wallColor\": \"$wallcolor\",\n");
+      $prophsh->{'wallColor'} = $wallcolor;
     }
     if (defined($roofcolor)) {
-      $roofcolor = JSON->new->allow_nonref->encode($roofcolor);
-      printwows("       \"roofColor\": \"$roofcolor\",\n");
+      $prophsh->{'roofColor'} = $roofcolor;
     }
     if (defined($roofshape)) {
       if ($roofshape eq 'pyramidal') { $roofshape = 'pyramid'; }
-      $roofshape = JSON->new->allow_nonref->encode($roofshape);
-      printwows("       \"roofShape\": \"$roofshape\",\n");
+      $prophsh->{'roofShape'} = $roofshape;
     }
     if (defined($roofmaterial)) {
-      $roofmaterial = JSON->new->allow_nonref->encode($roofmaterial);
-      printwows("       \"roofMaterial\": \"$roofmaterial\",\n");
+      $prophsh->{'roofMaterial'} = $roofmaterial;
     }
     if (defined($roofheight)) {
-      $roofheight = sprintf("%.1f", $roofheight);
-      printwows("       \"roofHeight\": \"$roofheight\",\n");
+      $prophsh->{'roofHeight'} = $roofheight;
     }
-    # this line is completely useless, but we need it to guarantee JSON has
-    # no trailing comma. osmbuildings will accept JSON with trailing commas
-    # just fine when you give it static, but NOT when requesting it dynamically.
-    printwows("       \"K9\": \"\"\n");
-    printwows("    }\n");
-    printwows("  }");
+    $jso->{'properties'} = $prophsh;
+    # Now dump it all.
+    print($crappyjsonoo->encode($jso));
   }
-  $sth2->finish();
-  undef($sth2);
+  $sth4->finish();
+  undef($sth4);
   $sth->finish();
   undef($sth);
 }
This page took 0.06661 seconds and 4 git commands to generate.