Переход с Google Maps на OpenStreetMap

в 11:54, , рубрики: android, googlemaps, OpenStreetMap, метки: , ,

Доброго времени суток. В нашем проекте так сложилось, что мы для нашей гео-вики стали использовать OSM а не GM. В ходе написания мобильного клиента под андроид был использован стандартный компонент MapView. Но, в ходе тестирования выявились расхождения в координатах OSM и GM, которые, в некоторых случаях, достигали 30 метров. Таким образом было решено переходить с карт гугла на OSM для чего и была найдена соответствующая библиотека osmdroid. О том, как переехать на эту библиотеку, расскажу под катом.

  • 1. Изменяем в layout файле класс com.google.android.maps.MapView на org.osmdroid.views.MapView
  • 2. В проекте для кластеризации меток на карте использовалась библиотека MarkerClusterer, все классы com.google.android.maps.* были заменены на org.osmdroid.*
    Например com.google.android.maps.GeoPoint на org.osmdroid.util.GeoPoint.
    Изза ошибки в коде osmdroid, в результате которой вызов mapView.getProjection().toPixels(...) иногда выдаёт координаты не экранные, а координаты тайла, в результате чего не работал метод hitTest класса ClusterMarker и не отрабатывала обработка клика по кластеру, был написан найден на stack overflow код, который преобразовывает координаты тайла в координаты экрана

    		/**
    		 * 
    		 * @param x  view coord relative to left
    		 * @param y  view coord relative to top
    		 * @param vw MapView
    		 * @return GeoPoint
    		 */
    
    		public static GeoPoint geoPointFromScreenCoords(int x, int y, MapView vw){
    		    if (x < 0 || y < 0 || x > vw.getWidth() || y > vw.getHeight()){
    		        return null; // coord out of bounds
    		    }
    		    // Get the top left GeoPoint
    		    Projection projection = vw.getProjection();
    		    GeoPoint geoPointTopLeft = (GeoPoint) projection.fromPixels(0, 0);
    		    Point topLeftPoint = new Point();
    		    // Get the top left Point (includes osmdroid offsets)
    		    projection.toPixels(geoPointTopLeft, topLeftPoint);
    		    // get the GeoPoint of any point on screen
    		    GeoPoint rtnGeoPoint = (GeoPoint) projection.fromPixels(x, y);
    		    return rtnGeoPoint;
    		}
    
    		/**
    		 * 
    		 * @param gp GeoPoint
    		 * @param vw Mapview
    		 * @return a 'Point' in screen coords relative to top left
    		 */
    
    		public static Point pointFromGeoPoint(GeoPoint gp, MapView vw){
    
    		    Point rtnPoint = new Point();
    		    Projection projection = vw.getProjection();
    		    projection.toPixels(gp, rtnPoint);
    		    // Get the top left GeoPoint
    		    GeoPoint geoPointTopLeft = (GeoPoint) projection.fromPixels(0, 0);
    		    Point topLeftPoint = new Point();
    		    // Get the top left Point (includes osmdroid offsets)
    		    projection.toPixels(geoPointTopLeft, topLeftPoint);
    		    rtnPoint.x-= topLeftPoint.x; // remove offsets
    		    rtnPoint.y-= topLeftPoint.y;
    		    if (rtnPoint.x > vw.getWidth() || rtnPoint.y > vw.getHeight() || 
    		            rtnPoint.x < 0 || rtnPoint.y < 0){
    		        return null; // gp must be off the screen
    		    }
    		    return rtnPoint;
    		}
    	

  • 3. В своё время был написан класс-обёртка вокруг стандартного MyLocationOverlay, был выброшен в результате перехода, т.к. в osm есть свой класс для этого.
  • 4. Не работал зум «щипком», для включения надо добавить вызов mapView.setMultiTouchControls(true);
  • 5. В качестве подложки была выбрана подложка «Mapnik» вызовом mapView.setTileSource(TileSourceFactory.MAPNIK);

В данный момент приложение проходит тестирование, в результате которого могут быть выявлены какие-то новые проблемы с этой библиотекой.

upd:

В ходе тестирования выяснилось, что если сначала выставлять координаты через setCenter(..), а потом приближать зум — попадали не в нужную точку (похоже на проблемы с точностью). В обратном случае всё работает отлично

Автор: Terranz

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js