2012-11-30 14 views
5

Ich habe eine Rails-App, die rgeo 0.3.19 mit proj4-Unterstützung verwendet und eine Verbindung zu einer PostGIS 1.5-Datenbank mit dem rgeo-activecord 0.4.5 gem. Herstellt.RGeo Projected Buffer Polygon zu klein

Meine App hat ein Modell namens Region, das einen geographischen Punkt, einen Radius und eine Polygonform enthält. Wenn eine neue Region gespeichert werden soll, verwendet sie die Pufferfunktion der Region, um ein Polygon mit dem Radius und dem geografischen Punkt zu erstellen.

Hier ist die geofactory die

für die Region Modell verwendet wird
GEOFACTORY = RGeo::Geographic.projected_factory(:buffer_resolution => 8, :projection_proj4 => '+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m [email protected] +wktext +no_defs', :projection_srid => 3857) 

Die projection_srid ich verwende, ist, dass von Apple und Google Mercator-Projektion 3857. Das Problem bildet, ist, dass der Puffer, der erstellt wird ist nicht die gleiche Größe wie die, die ich entweder in Apple Maps oder Google Maps zeichne. Zum Beispiel, wenn ich die in MapKit Funktion MKCircle gebaut verwenden

[MKCircle circleWithCenterCoordinate:self.coordinate radius:50]; 

Der Kreis wird so zeichnen und überlagert.

Aber wenn ich die Koordinaten nehme, die von der Pufferfunktion erzeugt wurden, die die Polygonform in der Datenbank bilden und sie auf Google Maps zeichne, bekomme ich diese.

GoogleMaps Radius

Wie Sie sehen können, das Polygon, das die gleiche Projektionssystem wurde unter Verwendung kleiner ist als es sein sollte. Dieses Problem wächst exponentiell aufgrund der Größe des definierten Radius außer Kontrolle. Ich habe auch versucht, die simple_mercator factory wie in RGeo definiert zu verwenden, die die gleichen Ergebnisse lieferte.

Hoffentlich hat jemand einen Einblick, warum, wenn ein geographischer Längen- und Breitengrad projiziert wird, ein falsch bemessenes Polygon entsteht.

Antwort

9

Was Sie hier beobachten, ist Mercator Verzerrung. Ein Abstand von "50" in einer Mercator-Projektion entspricht nicht 50 Metern auf der realen Planetenoberfläche, es sei denn, Sie befinden sich am Äquator.

Der von Ihrer iOS-Karte gezeichnete Kreis ist korrekt: das ist der 50-Meter-Radius. Ich vermute, dass Sie Ihr zweites Bild erstellt haben, indem Sie den Punkt in eine Mercator-Projektion projizieren (gemäß dem von Ihnen bereitgestellten Proj4). Anschließend haben Sie im projizierten Koordinatensystem einen Puffer mit Radius 50 erstellt. Allerdings entsprechen 50 Mercator-Einheiten bei 40,61 nur etwa 37,96 Meter der Erdoberfläche. Wenn Sie also dieses Polygon zurück in den Längen- und Breitengrad projizieren und es zeichnen, sehen Sie das: ein 38 Meter langer Kreis.

Eine Möglichkeit, dies zu visualisieren, ist die vollständige Weltkarte in Google Maps. Zeichnen Sie einen Kreis mit einem Radius von 50 Pixeln am Äquator. Zeichnen Sie dann einen weiteren Kreis mit einem Radius von 50 Pixeln über Grönland. Auf der Karte (in Mercator-Koordinaten) haben diese Kreise die gleiche Größe. Aber wenn Sie Ihre Mercator-Projektion kennen, wissen Sie, dass sie Grönland verzerrt, weil Grönland weit vom Äquator entfernt ist. Ihr Kreis über Grönland ist also im wirklichen Leben viel kleiner als Ihr Kreis über dem Äquator. Bei 40 Grad Breite ist die Verzerrung nicht so stark, aber sie ist immer noch da.

Wenn Sie das korrigieren möchten, ist es ziemlich einfach. Die durch die Mercator-Projektion verursachte Größenverzerrung ist proportional zur Sekante des Breitengrades. Das heißt, 50 Mercator-Einheiten auf dem Äquator entsprechen 50 Metern, aber 50 Mercator-Einheiten auf dem Breitengrad x (in Radianten) entsprechen 50/sec (x) Metern. Wenn Sie also einen Radius von 50 Metern wünschen, multiplizieren Sie 50 mal (Breitengrad) und verwenden Sie diese Zahl als Radius in Mercator-Koordinaten.In RGeo-Sprache:

p_lonlat = GEOFACTORY.point(40.610355377197266, -75.38220214843749) 
p_proj = p_lonlat.projection 
buf_proj = p_proj.buffer(50.0 * (1/Math.cos(p_lonlat.y/180.0 * Math::PI))) 
buf_lonlat = GEOFACTORY.unproject(buf_proj) 
+0

Danke für die Bearbeitung, MobileOverlord. Ich habe vergessen, dass es in Ruby keine Sekantenfunktion gab. Das ist, was ich bekomme, um Code zu veröffentlichen, ohne es zuerst zu testen ... :-) –