📜 ⬆️ ⬇️

Yandex maps without using Yandex Map Kit or maps from various sources in an Android application

Hello!
In this message we will consider the use of maps from various sources (including Yandex maps) in an Android application.
image
To solve the problem, we will use the osmdroid library. We will need the library itself (link not to the latest version, this version, it seems to me, is more stable and faster than the newer ones) and one more additional one . Add them to the project (used Eclipse under Windows) (Build Path-> Configure Buil Path-> Libraries-> Add External JARs).
Let's write the necessary permissions in the project manifest
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> 

To add new map sources, for example, Yandex and Google (Mapnik is already built into the library), you need to extend the XYTileSource class
MyTileSource
 public class MyTileSource extends XYTileSource { public MyTileSource(String aName, string aResourceId, int aZoomMinLevel, int aZoomMaxLevel, int aTileSizePixels, String aImageFilenameEnding, String... aBaseUrl) { super(aName, aResourceId, aZoomMinLevel, aZoomMaxLevel, aTileSizePixels, aImageFilenameEnding, aBaseUrl); } //  getTileURLString,        @Override public String getTileURLString(MapTile aTile) { return String.format(getBaseUrl(), aTile.getX(), aTile.getY(), aTile.getZoomLevel()); } } 


The problem is that osmdroid does not support the projection of Yandex maps. Therefore, for the correct display of Yandex maps, we will expand the TilesOverlay class. A class was found to convert coordinates to tiles and back online. Here is this slightly modified class.
YandexUtils
 public class YandexUtils { public static double[] geoToMercator(double[] g) { double d = g[0] * Math.PI / 180, m = g[1] * Math.PI / 180, l = 6378137, k = 0.0818191908426, f = k * Math.sin(m); double h = Math.tan(Math.PI / 4 + m / 2), j = Math.pow( Math.tan(Math.PI / 4 + Math.asin(f) / 2), k), i = h / j; // return new DoublePoint(Math.round(l * d), Math.round(l * // Math.log(i))); return new double[] { l * d, l * Math.log(i) }; } public static double[] mercatorToGeo(double[] e) { double j = Math.PI, f = j / 2, i = 6378137, n = 0.003356551468879694, k = 0.00000657187271079536, h = 1.764564338702e-8, m = 5.328478445e-11; double g = f - 2 * Math.atan(1 / Math.exp(e[1] / i)); double l = g + n * Math.sin(2 * g) + k * Math.sin(4 * g) + h * Math.sin(6 * g) + m * Math.sin(8 * g); double d = e[0] / i; return new double[] { d * 180 / Math.PI, l * 180 / Math.PI }; } public static double[] mercatorToTiles(double[] e) { double d = Math.round((20037508.342789 + e[0]) * 53.5865938), f = Math .round((20037508.342789 - e[1]) * 53.5865938); d = boundaryRestrict(d, 0, 2147483647); f = boundaryRestrict(f, 0, 2147483647); return new double[] { d, f }; } public static double[] tileToMercator(long[] d) { return new double[] { Math.round(d[0] / 53.5865938 - 20037508.342789), Math.round(20037508.342789 - d[1] / 53.5865938) }; } public static double[] tileCoordinatesToPixels(double[] i, int h) { double g = Math.pow(2, toScale(h)); return new double[] { (int) i[0] / g, (int) i[1] / g }; } public static double boundaryRestrict(double f, double e, double d) { return Math.max(Math.min(f, d), e); } public static int toScale(int i) { return 23 - i; } public static long[] getTile(double[] h, int i) { long e = 8; long j = toScale(i), g = (long) h[0] >> j, f = (long) h[1] >> j; return new long[] { g >> e, f >> e }; } public static long[] getPxCoordFromTileCoord(double[] h, int i) { long j = toScale(i), g = (long) h[0] >> j, f = (long) h[1] >> j; return new long[] { g, f }; } public static long[] getTileCoordFromPixCoord(long[] h, int i) { long j = toScale(i), g = h[0] << j, f = h[1] << j; return new long[] { g, f }; } public static double[] ReGetTile(double[] h, int i) { long e = 8; long j = toScale(i); long g = (long) h[0] << (int) j; long f = (long) h[1] << (int) j; double ge = g << (int) e; double fe = f << (int) e; long g2 = (long) (h[0] + 1) << (int) j; long f2 = (long) (h[1] + 1) << (int) j; double ge2 = g2 << (int) e; double fe2 = f2 << (int) e; double ad_g = (ge2 - ge) * (h[0] - Math.floor(h[0])); double ad_f = (fe2 - fe) * (h[1] - Math.floor(h[1])); return new double[] { ge + ad_g, fe + ad_f }; } public static double[] ReGetTile(long[] h, int i) { long e = 8; long j = toScale(i); long g = (long) h[0] << (int) j; long f = (long) h[1] << (int) j; return new double[] { g << (int) e, f << (int) e }; } public static double[] getGeoFromTile(int x, int y, int zoom) { double a, c1, c2, c3, c4, g, z, mercX, mercY; a = 6378137; c1 = 0.00335655146887969; c2 = 0.00000657187271079536; c3 = 0.00000001764564338702; c4 = 0.00000000005328478445; mercX = (x * 256 * 2 ^ (23 - zoom)) / 53.5865938 - 20037508.342789; mercY = 20037508.342789 - (y * 256 * 2 ^ (23 - zoom)) / 53.5865938; g = Math.PI / 2 - 2 * Math.atan(1 / Math.exp(mercY / a)); z = g + c1 * Math.sin(2 * g) + c2 * Math.sin(4 * g) + c3 * Math.sin(6 * g) + c4 * Math.sin(8 * g); return new double[] { mercX / a * 180 / Math.PI, z * 180 / Math.PI }; } public static long[] getTileFromGeo(double lat, double lon, int zoom) { double rLon, rLat, a, k, z; rLon = lon * Math.PI / 180; rLat = lat * Math.PI / 180; a = 6378137; k = 0.0818191908426; z = Math.pow( Math.tan(Math.PI / 4 + rLat / 2) / (Math.tan(Math.PI / 4 + Math.asin(k * Math.sin(rLat)) / 2)), k); return new long[] { (int) (((20037508.342789 + a * rLon) * 53.5865938 / Math.pow(2, (23 - zoom))) / 256), (int) (((20037508.342789 - a * Math.log(z)) * 53.5865938 / Math .pow(2, (23 - zoom)))) / 256 }; } public static double tile2lon(int x, int aZoom) { return (x / Math.pow(2.0, aZoom) * 360.0) - 180; } public static double tile2lat(int y, int aZoom) { final double MerkElipsK = 0.0000001; final long sradiusa = 6378137; final long sradiusb = 6356752; final double FExct = (double) Math.sqrt(sradiusa * sradiusa - sradiusb * sradiusb) / sradiusa; final int TilesAtZoom = 1 << aZoom; double result = (y - TilesAtZoom / 2) / -(TilesAtZoom / (2 * Math.PI)); result = (2 * Math.atan(Math.exp(result)) - Math.PI / 2) * 180 / Math.PI; double Zu = result / (180 / Math.PI); double yy = ((y) - TilesAtZoom / 2); double Zum1 = Zu; Zu = Math.asin(1 - ((1 + Math.sin(Zum1)) * Math.pow(1 - FExct * Math.sin(Zum1), FExct)) / (Math.exp((2 * yy) / -(TilesAtZoom / (2 * Math.PI))) * Math .pow(1 + FExct * Math.sin(Zum1), FExct))); while (Math.abs(Zum1 - Zu) >= MerkElipsK) { Zum1 = Zu; Zu = Math .asin(1 - ((1 + Math.sin(Zum1)) * Math.pow( 1 - FExct * Math.sin(Zum1), FExct)) / (Math.exp((2 * yy) / -(TilesAtZoom / (2 * Math.PI))) * Math .pow(1 + FExct * Math.sin(Zum1), FExct))); } result = Zu * 180 / Math.PI; return result; } public static int[] getMapTileFromCoordinates(final double aLat, final double aLon, final int zoom) { final int[] out = new int[2]; final double E2 = (double) aLat * Math.PI / 180; final long sradiusa = 6378137; final long sradiusb = 6356752; final double J2 = (double) Math.sqrt(sradiusa * sradiusa - sradiusb * sradiusb) / sradiusa; final double M2 = (double) Math.log((1 + Math.sin(E2)) / (1 - Math.sin(E2))) / 2 - J2 * Math.log((1 + J2 * Math.sin(E2)) / (1 - J2 * Math.sin(E2))) / 2; final double B2 = (double) (1 << zoom); out[0] = (int) Math.floor(B2 / 2 - M2 * B2 / 2 / Math.PI); out[1] = (int) Math.floor((aLon + 180) / 360 * (1 << zoom)); return out; } } 


Now that there are formulas for converting coordinates, we can expand TilesOverlay
YandexTilesOverlay
 public class YandexTilesOverlay extends TilesOverlay { private final Rect mTileRect = new Rect(); private final Point mTilePos = new Point(); public YandexTilesOverlay(MapTileProviderBase aTileProvider, Context aContext) { super(aTileProvider, aContext); } //  draw   Yandex  @Override protected void draw(Canvas c, MapView osmv, boolean shadow) { //  int zoom = osmv.getZoomLevel(); final Projection pj = osmv.getProjection(); //     BoundingBoxE6 bb = osmv.getBoundingBox(); //        double[] MercatorTL = YandexUtils.geoToMercator(new double[] { bb.getLonWestE6() / 1E6, bb.getLatNorthE6() / 1E6 }); double[] TilesTL = YandexUtils.mercatorToTiles(MercatorTL); long[] TileTL = YandexUtils.getTile(TilesTL, zoom); double[] MercatorRB = YandexUtils.geoToMercator(new double[] { bb.getLonEastE6() / 1E6, bb.getLatSouthE6() / 1E6 }); double[] TilesRB = YandexUtils.mercatorToTiles(MercatorRB); long[] TileRB = YandexUtils.getTile(TilesRB, zoom); mTileProvider .ensureCapacity((int) ((TileRB[1] - TileTL[1] + 1) * (TileRB[0] - TileTL[0] + 1))); //    Yandex double[] reTiles = YandexUtils.ReGetTile(new long[] { TileTL[0], TileTL[1] }, zoom); long xx = (long) reTiles[0]; long yy = (long) reTiles[1]; double[] reMercator = YandexUtils.tileToMercator(new long[] { xx, yy }); double[] tmp = YandexUtils.mercatorToGeo(reMercator); //    Yandex     osmdroid GeoPoint gp = new GeoPoint(tmp[1], tmp[0]); pj.toPixels(gp, mTilePos); //      Yandex for (int y = (int) TileTL[1]; y <= TileRB[1]; y++) { int xcount = 0; for (int x = (int) TileTL[0]; x <= TileRB[0]; x++) { final MapTile tile = new MapTile(zoom, x, y); final Drawable currentMapTile = mTileProvider.getMapTile(tile); if (currentMapTile != null) { mTileRect.set(mTilePos.x, mTilePos.y, mTilePos.x + 256, mTilePos.y + 256); currentMapTile.setBounds(mTileRect); currentMapTile.draw(c); } xcount++; mTilePos.x += 256; } mTilePos.x -= xcount * 256; mTilePos.y += 256; } } } 


It remains to withdraw the cards on the form
BlogOsmYandexActivity
 public class BlogOsmYandexActivity extends Activity { MapView mMap; MapController mMapController; YandexTilesOverlay tilesOverlayYandex; TilesOverlay tilesOverlayGoogle; TilesOverlay tilesOverlayMapnik; MapTileProviderBasic tileProviderYandex; MapTileProviderBasic tileProviderGoogle; MapTileProviderBasic tileProviderMapnik; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // RelativeLayout rl = new RelativeLayout(this); // mMap = new MapView(this, 256); //     mMap.setBuiltInZoomControls(true); //    rl.addView(mMap, new RelativeLayout.LayoutParams( LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT)); setContentView(rl); //       mMapController = mMap.getController(); mMapController.setZoom(15); mMapController.setCenter(new GeoPoint(55.751893, 37.617166)); //   Yandex //    firebug //getTileURLString   %s     ITileSource tileSourceYandex = new MyTileSource( "YandexMap", null, 0, 23, 256, ".png", "http://vec04.maps.yandex.net/tiles?l=map&v=2.28.0&x=%s&y=%s&z=%s&lang=ru-RU", "http://vec03.maps.yandex.net/tiles?l=map&v=2.28.0&x=%s&y=%s&z=%s&lang=ru-RU", "http://vec02.maps.yandex.net/tiles?l=map&v=2.28.0&x=%s&y=%s&z=%s&lang=ru-RU", "http://vec01.maps.yandex.net/tiles?l=map&v=2.28.0&x=%s&y=%s&z=%s&lang=ru-RU"); //   Google ITileSource tileSourceGoogle = new MyTileSource( "Google-Map", null, 0, 23, 256, ".png", "http://mt0.google.com/vt/lyrs=m&hl=ru&x=%s&y=%s&z=%s&s=Galileo", "http://mt1.google.com/vt/lyrs=m&hl=ru&x=%s&y=%s&z=%s&s=Galileo", "http://mt2.google.com/vt/lyrs=m&hl=ru&x=%s&y=%s&z=%s&s=Galileo", "http://mt3.google.com/vt/lyrs=m&hl=ru&x=%s&y=%s&z=%s&s=Galileo"); //         Yandex tileProviderYandex = new MapTileProviderBasic(getApplicationContext()); tileProviderYandex.setTileSource(tileSourceYandex); tileProviderYandex.setTileRequestCompleteHandler(mMap .getTileRequestCompleteHandler()); //  Yandex  tilesOverlayYandex = new YandexTilesOverlay(tileProviderYandex, this.getBaseContext()); //         Google tileProviderGoogle = new MapTileProviderBasic(getApplicationContext()); tileProviderGoogle.setTileRequestCompleteHandler(mMap .getTileRequestCompleteHandler()); tileProviderGoogle.setTileSource(tileSourceGoogle); //  Google  tilesOverlayGoogle = new TilesOverlay(tileProviderGoogle, this.getBaseContext()); //         Mapnik tileProviderMapnik = new MapTileProviderBasic(getApplicationContext()); tileProviderMapnik.setTileRequestCompleteHandler(mMap .getTileRequestCompleteHandler()); tileProviderMapnik.setTileSource(TileSourceFactory.MAPNIK); //  Mapnik  tilesOverlayMapnik = new TilesOverlay(tileProviderMapnik, this.getBaseContext()); // Yandex    mMap.getOverlayManager().setTilesOverlay(tilesOverlayYandex); } //   / @Override public boolean onCreateOptionsMenu(Menu menu) { SubMenu sm = menu.addSubMenu(" "); sm.add(0, 111, Menu.NONE, ""); sm.add(0, 222, Menu.NONE, "Google"); sm.add(0, 333, Menu.NONE, "Mapnik"); return true; } //      / @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case (111): mMap.getOverlayManager().setTilesOverlay(tilesOverlayYandex); mMap.invalidate(); return true; case (222): mMap.getOverlayManager().setTilesOverlay(tilesOverlayGoogle); mMap.invalidate(); return true; case (333): mMap.getOverlayManager().setTilesOverlay(tilesOverlayMapnik); mMap.invalidate(); return true; default: return super.onOptionsItemSelected(item); } } } 


Result
image

')

Source: https://habr.com/ru/post/151103/


All Articles