Як обчислити оптимальний рівень збільшення для відображення двох або більше точок на карті


12

Ми хочемо відобразити кілька маркерів на статичній карті і хочемо обчислити оптимальний рівень масштабування, як це робить Google Maps. Ми вже розраховували обмежуючий прямокутник і центральну точку на карті, але тепер нам важко розрахувати правильний масштаб, щоб відобразити весь обмежуючий прямокутник. Чи може хтось, будь ласка, вказати нас у правильному напрямку?


Це не дуже складно, але чому б не просто помножити MBR на 120% і збільшити масштаб до цього? Все інше залежить від вашого визначення "оптимального", чи не так?
ThomM

+1 до ідеї ThomM Саме цим займається ArcGIS.
Рагі Ясер Бурхум

1
Вибачте, але що це MBR?
Родріго

Відповіді:


5

Щоб отримати рівень масштабування, вам потрібно знати розміри пікселів вашої карти. Вам також потрібно буде провести математику в сферичних координатах Меркатора.

  1. Перетворити широту, довготу в сферичний Меркатор х, у.
  2. Отримайте відстань між вашими двома точками у сферичному меркаторі.
  3. Екватор прогнозується близько 40 м, а плитки шириною 256 пікселів, тому довжина пікселів цієї карти при заданому рівні збільшення становить приблизно 256 * відстань / 40000000 * 2 ^ зум . Спробуйте zoom = 0, zoom = 1, zoom = 2, поки відстань не буде занадто довгою для розмірів пікселів вашої карти.

1
Сподіваюся, я віддалено зрозумів, що ви насправді просите. = \
Міхал Мігурський

Знайшов це питання, шукаючи цю відповідь, дякую.
БенджамінГолдер

@MichalMigurski Ви можете пояснити, як обчислити відстань між 2-ма точками у сферичному меркаторі? особливо x координати ... я застряг спасибі.
otmezger

5

Це код C #, який я використовую в 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: обмежувальний ящик у довгих / lat координатах (x = довгий, y = lat)
  • paddingFactor: це можна використовувати для отримання ефекту "120%", на який посилається ThomM. Значення 1,2 отримає 120%.

Зауважте, що в моєму випадку zoomможе бути реальна кількість. Що стосується веб-карт, вам потрібно ціле значення масштабу, тому вам слід скористатися чимось на кшталт, (int)Math.Floor(zoom)щоб отримати його.

Зрозуміло, цей код стосується лише прогнозування на веб-Меркатор.


1
Дякую за це Чи доступний код для CenterMapOnPoint?
mcintyre321

Ідеально! Я використовував це для обчислення масштабу BoundingBox в OsmDroid SDK, і він працює :)
Billda

Де я можу знайти ці бібліотеки? Я не можу знайти збірки GeometryUtils або Bounds2. Чи потрібно мені щось завантажувати з maperitive? Я бачу лише додаток там.
Dowlers

@Dowlers GeometryUtilsпросто використовується тут для перетворення градусів в радіани і назад, це проста математична формула. Bounds2в основному просто структура прямокутника. Цей код був скоріше псевдокодом, ніж чимось безпосередньо копіюваним.
Ігор Брейц

1
Це дивовижно @ IgorBrejc- дуже дякую! Я перетворив його на python тут, якщо інші пишуть код python: gist.github.com/mappingvermont/d534539fa3ebe4a1e242644e528bf7b9
Чарлі Хофманн

1

Якщо ви використовуєте OpenLayers, тоді Map.getZoomForExtentбуде обчислено найвищий рівень масштабування, який може відповідати всьому масштабу на карті. В extentпотреби бути в проекції карти. Ви також можете використовувати a, fill_factorщоб уникнути відображення точок на краю карти та max_zoomдля обмеження можливого масштабування:

extent = extent.scale(1/fill_ratio);
var zoom = Math.min(map.getZoomForExtent(map_extent), max_zoom);
map.setCenter(extent.getCenterLonLat(), zoom);

це не вирішило мою проблему, але це призвело до пошуку методу fitBounds Leaflet, який врятував мені день.
СамуельДев
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.