Jak obliczyć optymalny poziom powiększenia, aby wyświetlić dwa lub więcej punktów na mapie

12

Chcemy wyświetlić kilka znaczników na mapie statycznej i chcemy obliczyć optymalny poziom powiększenia, tak jak robi to Google Maps. Obliczyliśmy już prostokąt obwiedni i punkt środkowy mapy, ale teraz mamy trudności z obliczeniem prawidłowego poziomu powiększenia, aby wyświetlić cały prostokąt obwiedni. Czy ktoś może skierować nas w dobrym kierunku?

Tobias Schwarz
źródło
Nie jest to zbyt skomplikowane, ale dlaczego nie pomnożyć MBR o 120% i powiększyć do tego? Wszystko inne zależy od twojej definicji „optymalnej”, prawda?
ThomM,
+1 do pomysłu ThomM. Właśnie to robi ArcGIS.
Ragi Yaser Burhum
1
Przepraszam, ale co to jest MBR?
Rodrigo

Odpowiedzi:

5

Aby uzyskać poziom powiększenia, musisz znać wymiary w pikselach mapy. Musisz także wykonać matematykę w sferycznych współrzędnych merkatora.

  1. Przelicz szerokość, długość na sferyczny merkator x, y.
  2. Uzyskaj odległość między dwoma punktami w kulistym mercatorze.
  3. Równik ma rzut około 40 metrów, a płytki mają szerokość 256 pikseli, więc długość piksela tej mapy przy danym poziomie powiększenia wynosi około 256 * odległość / 40000000 * 2 ^ zbliżenie . Spróbuj powiększyć = 0, zoom = 1, zoom = 2, aż odległość będzie zbyt długa, aby pomieścić wymiary mapy w pikselach.
Michał Migurski
źródło
1
Mam nadzieję, że zdalnie zrozumiałem, o co właściwie pytasz. = \
Michał Migurski
Znalazłem to pytanie, szukając tej odpowiedzi, dzięki.
BenjaminGolder
@MichalMigurski czy możesz wyjaśnić, jak obliczyć odległość między 2 punktami w sferycznym merkatorze? szczególnie współrzędne x ... utknąłem dzięki.
otmezger
5

Oto kod C #, którego używam w Maperitive :

    public void ZoomToArea (Bounds2 mapArea, float paddingFactor)
    {
        double ry1 = Math.Log((Math.Sin(GeometryUtils.Deg2Rad(mapArea.MinY)) + 1) 
            / Math.Cos(GeometryUtils.Deg2Rad(mapArea.MinY)));
        double ry2 = Math.Log((Math.Sin(GeometryUtils.Deg2Rad(mapArea.MaxY)) + 1) 
            / Math.Cos(GeometryUtils.Deg2Rad(mapArea.MaxY)));
        double ryc = (ry1 + ry2) / 2;
        double centerY = GeometryUtils.Rad2Deg(Math.Atan(Math.Sinh(ryc)));

        double resolutionHorizontal = mapArea.DeltaX / Viewport.Width;

        double vy0 = Math.Log(Math.Tan(Math.PI*(0.25 + centerY/360)));
        double vy1 = Math.Log(Math.Tan(Math.PI*(0.25 + mapArea.MaxY/360)));
        double viewHeightHalf = Viewport.Height/2.0f;
        double zoomFactorPowered = viewHeightHalf
            / (40.7436654315252*(vy1 - vy0));
        double resolutionVertical = 360.0 / (zoomFactorPowered * 256);

        double resolution = Math.Max(resolutionHorizontal, resolutionVertical) 
            * paddingFactor;
        double zoom = Math.Log(360 / (resolution * 256), 2);
        double lon = mapArea.Center.X;
        double lat = centerY;

        CenterMapOnPoint(new PointD2(lon, lat), zoom);
    }
  • mapArea: obwiednia w długich / długich współrzędnych (x = długi, y = lat)
  • paddingFactor: można tego użyć, aby uzyskać efekt „120%”, o którym mówi ThomM. Wartość 1,2 da ci 120%.

Zauważ, że w moim przypadku zoommoże być liczbą rzeczywistą. W przypadku map internetowych potrzebna jest całkowita wartość powiększenia, więc powinieneś użyć czegoś takiego jak (int)Math.Floor(zoom)to.

Oczywiście ten kod dotyczy tylko projekcji Web Mercator.

Igor Brejc
źródło
1
Dzięki za to. Czy dostępny jest kod CenterMapOnPoint?
mcintyre321
Doskonały! Użyłem tego, aby obliczyć powiększenie BoundingBox w OsmDroid SDK i działa :)
Billda
Gdzie znajdę te biblioteki? Nie mogę znaleźć zespołów GeometryUtils ani Bounds2. Czy muszę pobrać coś z programu maperitive? Widzę tam tylko aplikację.
Dowlers
@Dowlers GeometryUtilssłuży tutaj do konwersji stopni na radiany i odwrotnie, jest to prosta formuła matematyczna. Bounds2jest po prostu strukturą prostokąta. Ten kod został dostarczony bardziej jako pseudokod niż coś, co można bezpośrednio skopiować.
Igor Brejc
1
To jest niesamowite @ IgorBrejc - dziękuję bardzo! Przekształciłem go tutaj w python, na wypadek, gdyby inni pisali kod python: gist.github.com/mappingvermont/d534539fa3ebe4a1e242644e528bf7b9
Charlie Hofmann
1

Jeśli używasz OpenLayers Map.getZoomForExtent, obliczy najwyższy poziom powiększenia, który może zmieścić się w całym zakresie na mapie. Że extentmusi być w projekcji mapy. Możesz także użyć a, fill_factoraby uniknąć wyświetlania punktów na krawędzi mapy i max_zoomograniczyć możliwy zoom:

extent = extent.scale(1/fill_ratio);
var zoom = Math.min(map.getZoomForExtent(map_extent), max_zoom);
map.setCenter(extent.getCenterLonLat(), zoom);
Alex Morega
źródło
to nie rozwiązało mojego problemu, ale doprowadziło mnie do znalezienia metody fitBounds Leafleta, która uratowała mi dzień.
SamuelDev