I am developing an app using map static api in android
this is the business logic, get user location request from google static api with this location and draw a circle around this location
this is the code I am using
https://maps.googleapis.com/maps/api/staticmap?center=29.31166,47.481766&zoom=7&size=600x300&maptype=roadmap&key=My Key
the problem now how to draw a circle around it, I searched and I found it is done using path, but could not understand how to get that path
You need just draw path as in Developers Guide:
http://maps.googleapis.com/maps/api/staticmap?center=29.31166,47.48177&zoom=7&size=600x300&path=color:0x0000FFFF|weight:3|fillcolor:0x0000FF77|<FIRST_POINT_LAT>,<FIRST_POINT_LNG>|<SECOND_POINT_LAT>,<SECOND_POINT_LNG>|...|<LAST_POINT_LAT>,<LAST_POINT_LNG>&key=<YOUR_API_KEY>
where <FIRST_POINT_LAT>,<FIRST_POINT_LNG>|<SECOND_POINT_LAT>,<SECOND_POINT_LNG>|...|<LAST_POINT_LAT>,<LAST_POINT_LNG> is coordinates of your circle path. For it's calculation you can use method like that:
private List<LatLng> getCirclePoints(LatLng center, double radius) {
List<LatLng> circlePoints = new ArrayList<>();
// convert center coordinates to radians
double lat_rad = Math.toRadians(center.latitude);
double lon_rad = Math.toRadians(center.longitude);
double dist = radius / 6378137;
// calculate circle path point for each 5 degrees
for (int deg = 0; deg < 360; deg += 5) {
double rad = Math.toRadians(deg);
// calculate coordinates of next circle path point
double new_lat = Math.asin(Math.sin(lat_rad) * Math.cos(dist) + Math.cos(lat_rad) * Math.sin(dist) * Math.cos(rad));
double new_lon = lon_rad + Math.atan2(Math.sin(rad) * Math.sin(dist) * Math.cos(lat_rad), Math.cos(dist)
- Math.sin(lat_rad) * Math.sin(new_lat));
// convert new lat and lon to degrees
double new_lat_deg = Math.toDegrees(new_lat);
double new_lon_deg = Math.toDegrees(new_lon);
circlePoints.add(new LatLng(new_lat_deg, new_lon_deg));
}
return circlePoints;
}
And you can format Static Maps API URL with that points this way:
private String buildStaticApiUrlWithCircle(LatLng mapCenter, int zoom, int width, int height,
LatLng circleCenter, double circleRadius, int pathWeight, String pathColor, String fillColor) {
List<LatLng> circlePoints =getCirclePoints(circleCenter, circleRadius);
StringBuilder url = new StringBuilder();
url.append("http://maps.googleapis.com/maps/api/staticmap?");
url.append(String.format("center=%8.5f,%8.5f", mapCenter.latitude, mapCenter.longitude));
url.append(String.format("&zoom=%d", zoom));
url.append(String.format("&size=%dx%d", width, height));
// set circle path properties
url.append(String.format("&path="));
url.append(String.format("color:%s", pathColor));
url.append(String.format("|weight:%d", pathWeight));
url.append(String.format("|fillcolor:%s", fillColor));
// add circle path points
for (LatLng point : circlePoints) {
url.append(String.format("|%8.5f,%8.5f", point.latitude, point.longitude));
}
// add API key to URL
url.append(String.format("&key=%s", <YOUR_API_KEY>)));
return url.toString();
}
Circle path and fill colours should be set as String in "0xRRGGBBAA" format, where RR - value of red channel, GG - value of green channel, BB - value of blue channel and AA - value of alpha channel (e.g. "0x0000FFFF" - pure blue with no transparency, "0xFF000077" - pure red 50% transparent and so on).
When you use buildStaticApiUrlWithCircle() this way:
...
int mapZoom = 7;
int mapWidth = 600;
int mapHeight = 300;
LatLng mapCenter = new LatLng(29.31166, 47.481766);
LatLng circleCenter = new LatLng(29.376297, 47.976379);
double circleRadiusMerers = 35000;
String circlePathColor = "0x0000FFFF";
String circleFillColor = "0x0000FF99";
String mapUrl = buildStaticApiUrlWithCircle(mapCenter, mapZoom, mapWidth, mapHeight,
circleCenter, circleRadiusMerers, 3, circlePathColor, circleFillColor);
try {
Bitmap mapBitmap = new GetStaticMapAsyncTask().execute(mapUrl).get();
mMapImageView.setImageBitmap(mapBitmap);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
...
where GetStaticMapAsyncTask is:
private class GetStaticMapAsyncTask extends AsyncTask<String, Void, Bitmap> {
protected void onPreExecute() {
super.onPreExecute();
}
protected Bitmap doInBackground(String... params) {
Bitmap bitmap = null;
HttpURLConnection connection = null;
try {
URL url = new URL(params[0]);
connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
connection.connect();
int responseCode = connection.getResponseCode();
InputStream stream = connection.getInputStream();
bitmap = BitmapFactory.decodeStream(stream);
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (connection != null) {
connection.disconnect();
}
}
return bitmap;
}
#Override
protected void onPostExecute(Bitmap result) {
super.onPostExecute(result);
}
}
you'll got something like that:
Also, you can use Google Maps Lite Mode instead of Static Map API (Lite Mode supports drawing circles). Or even, if you need draw circle exactly at the center of the map - direct drawing on bitmap canvas. For example you can modify doInBackground() of GetStaticMapAsyncTask this way:
protected Bitmap doInBackground(String... params) {
Bitmap bitmap = null;
HttpURLConnection connection = null;
try {
URL url = new URL(params[0]);
connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
connection.connect();
int responseCode = connection.getResponseCode();
InputStream stream = connection.getInputStream();
Bitmap mapBitmap = BitmapFactory.decodeStream(stream);
Paint locaionMarkerPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
locaionMarkerPaint.setColor(Color.BLUE);
bitmap = Bitmap.createBitmap(mapBitmap.getWidth(), mapBitmap.getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
canvas.drawBitmap(mapBitmap,0,0, null);
canvas.drawCircle(mapBitmap.getWidth()/ 2, mapBitmap.getHeight() / 2, 20, locaionMarkerPaint);
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (connection != null) {
connection.disconnect();
}
}
return bitmap;
}
Related
Is there any posibility to show Google Maps if you are offline in your own App?
What about if I download an Area FROM Google Maps application for offline mode, could i visualize the map on the app that i develop if i don't have internet connection?
if not, What options do i have to make this possible? I just want to visualize the map when my app is offline...
The following its the code that this post provided TileProvider using local tiles
#Override
public Tile getTile(int x, int y, int zoom) {
byte[] image = readTileImage(x, y, zoom);
return image == null ? null : new Tile(TILE_WIDTH, TILE_HEIGHT,image);
}
private byte[] readTileImage(int x,int y, int zoom){
InputStream is= null;
ByteArrayOutputStream buffer= null;
try{
is= mAssets.open(getTileFileName(x,y,zoom));
buffer= new ByteArrayOutputStream();
int nRead;
byte[] data= new byte[BUFFER_SIZE];
while ((nRead= is.read(data,0,BUFFER_SIZE)) !=-1){
buffer.write(data,0,nRead);
}
buffer.flush();
return buffer.toByteArray();
}
catch(IOException ex){
Log.e("LINE 60 CustomMap", ex.getMessage());
return null;
}catch(OutOfMemoryError e){
Log.e("LINE 64 CustomMap", e.getMessage());
return null;
}finally{
if(is!=null){
try{
is.close();
} catch (IOException e) {}
}
if(buffer !=null){
try{
buffer.close();
}catch (Exception e){}
}
}
}
private String getTileFileName(int x, int y, int zoom){
return "map/"+ zoom +'/' +x+ '/'+y+".png";
}
I was looking for information, and My questions is, how can i download the tiles?
I was facing the same challenge, and none of the examples I found included a complete implementation of downloading the tiles, writing them to file and reading them from file.
This is my code, which reads the tile from file when it's available locally and downloads/saves the tile when not. This uses the OpenStreetMap.org tile server, but you could use any server you like by changing the URL.
private class OfflineTileProvider implements TileProvider {
private static final String TILES_DIR = "your_tiles_directory/";
private static final int TILE_WIDTH = 256;
private static final int TILE_HEIGHT = 256;
private static final int BUFFER_SIZE_FILE = 16384;
private static final int BUFFER_SIZE_NETWORK = 8192;
private ConnectivityManager connectivityManager;
#Override
public Tile getTile(int x, int y, int z) {
Log.d(TAG, "OfflineTileProvider.getTile(" + x + ", " + y + ", " + z + ")");
try {
byte[] data;
File file = new File(TILES_DIR + z, x + "_" + y + ".png");
if (file.exists()) {
data = readTile(new FileInputStream(file), BUFFER_SIZE_FILE);
} else {
if (connectivityManager == null) {
connectivityManager = (ConnectivityManager) getSystemService(
Context.CONNECTIVITY_SERVICE);
}
NetworkInfo activeNetworkInfo = connectivityManager.getActiveNetworkInfo();
if (activeNetworkInfo == null || !activeNetworkInfo.isConnected()) {
Log.w(TAG, "No network");
return NO_TILE;
}
Log.d(TAG, "Downloading tile");
data = readTile(new URL("https://a.tile.openstreetmap.org/" +
z + "/" + x + "/" + y + ".png").openStream(),
BUFFER_SIZE_NETWORK);
try (OutputStream out = new BufferedOutputStream(new FileOutputStream(file))) {
out.write(data);
}
}
return new Tile(TILE_WIDTH, TILE_HEIGHT, data);
} catch (Exception ex) {
Log.e(TAG, "Error loading tile", ex);
return NO_TILE;
}
}
private byte[] readTile(InputStream in, int bufferSize) throws IOException {
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
try {
int i;
byte[] data = new byte[bufferSize];
while ((i = in.read(data, 0, bufferSize)) != -1) {
buffer.write(data, 0, i);
}
buffer.flush();
return buffer.toByteArray();
} finally {
in.close();
buffer.close();
}
}
}
Replace "your_tiles_directory" with the path to the directory where you want to store your tiles.
To use the TileProvider:
map.setMapType(GoogleMap.MAP_TYPE_NONE);
offlineTileOverlay = map.addTileOverlay(new TileOverlayOptions()
.tileProvider(new OfflineTileProvider()));
Edit: You may want to set the max zoom level, the default is 21 but OpenStreetMap for example seems to have a maximum of 19.
map.setMaxZoomPreference(19);
You can download tile image from a tile server and cache on your app. Check some server on this link. Or you can build a tile as this demo, then download tile image from it.
Good luck
I've set custom overlays on Google Maps to get indoor maps of my building. But when I move the map on the screen, buildings are moving and overlays are fixed. So it gets a bit ugly. You can see how ugly it is on the image below.
ugly indoor map
I'd like to get the map like this : pretty indoor map
How to prevent buildings from moving on Android Google Maps API ?
Below the part of code I use to set overlays on the map :
private GoogleMap mMap;
private MapView mapView;
#Override
public void onMapReady(GoogleMap googleMap) {
this.mMap = googleMap;
refreshGoogleMap();
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_maps);
mapView = (MapView) findViewById(R.id.mapView);
mapView.onCreate(savedInstanceState);
mapView.getMapAsync(this);
}
private void refreshGoogleMap() {
mMap.clear();
mMap.addTileOverlay(new TileOverlayOptions()
.tileProvider(new AssetsTileProvider())
.zIndex(-1));
}
private class AssetsTileProvider implements TileProvider {
private static final int TILE_WIDTH = 256;
private static final int TILE_HEIGHT = 256;
private static final int BUFFER_SIZE = 16 * 1024;
#Override
public Tile getTile(int x, int y, int zoom) {
byte[] bytes = readFile(x, y, zoom);
return bytes == null ? NO_TILE : new Tile(TILE_WIDTH, TILE_HEIGHT, bytes);
}
private byte[] readFile(int x, int y, int zoom) {
InputStream in = null;
ByteArrayOutputStream buffer = null;
try {
in = getAssets().open(getFilename(x, y, zoom));
buffer = new ByteArrayOutputStream();
int read;
byte[] data = new byte[BUFFER_SIZE];
while ((read = in.read(data, 0, BUFFER_SIZE)) != -1) {
buffer.write(data, 0, read);
}
buffer.flush();
return buffer.toByteArray();
} catch (Exception e) {
// NO OP
} finally {
if (in != null) try { in.close(); } catch (Exception ignored) {}
if (buffer != null) try { buffer.close(); } catch (Exception ignored) {}
}
return null;
}
private String getFilename(int x, int y, int zoom) {
String level = currentLevel == LEVEL_1 ? "level-1" : "level-2";
return level + "/" + zoom + "/" + x + "/" + y + ".png";
}
}
I would like to access some Custom Map Tiles when creating a TileOverlay for Google Maps API.
So this is my current code:
TileProvider tileProvider = new UrlTileProvider(256, 256) {
#Override
public URL getTileUrl(int x, int y, int z) {
String url = String.format("https://api.mycustommaps.com/v1/%d/%d/%d.jpg", z, x, y);
if (!checkTileExists(x, y, z)) {
return null;
}
try {
URL tileUrl = new URL(url);
tileUrl.openConnection().addRequestProperty("Authorization", LOGIN_TOKEN);
return tileUrl;
} catch (MalformedURLException e) {
e.printStackTrance();
} catch (IOException e) {
e.printStackTrance();
}
return null;
}
};
Since the connection returns 401 Anauthorized, I can't access the tiles. How could I pass Authorization header to let the url know I am authorized to access those tiles?
you have to implement the "TileProvider" interface, not URLTileProvider (because you have to retrieve the tile on your own, an URL is not enough.
https://developers.google.com/android/reference/com/google/android/gms/maps/model/TileProvider
as you can see, there is a note to keep attention:
Calls to methods in this interface might be made from multiple threads so implementations of this interface must be threadsafe.
and you have to implement a single method:
abstract Tile
getTile(int x, int y, int zoom)
It is now your work download the tile, I've done it for local files, so I'm just writing here some code that might need some more refinement and testing:
#Override
public Tile getTile(int x, int y, int zoom) {
String url = String.format("https://api.mycustommaps.com/v1/%d/%d/%d.jpg", z, x, y);
if (!checkTileExists(x, y, z)) {
return null;
}
try {
URL tileUrl = new URL(url);
//Download the PNG as byte[], I suggest using OkHTTP library or see next code!
final byte[] data = downloadData(tileUrl);
final int height = tileheight;
final int width = tilewidth;
if (data != null) {
if (BuildConfig.DEBUG)Log.d(TAG, "Cache hit for tile " + key);
return new Tile(width, height, data);
}
//In this case error, maybe return a placeholder tile or TileProvider.NO_TILE
} catch (MalformedURLException e) {
e.printStackTrance();
} catch (IOException e) {
e.printStackTrance();
}
}
to download:
byte[] downloadData(URL url){
ByteArrayOutputStream baos = new ByteArrayOutputStream();
InputStream is = null;
try {
tileUrl.openConnection().addRequestProperty("Authorization", LOGIN_TOKEN);
is = url.openStream();
byte[] byteChunk = new byte[4096]; // Or whatever size you want to read in at a time.
int n;
while ( (n = is.read(byteChunk)) > 0 ) {
baos.write(byteChunk, 0, n);
}
}
catch (IOException e) {
System.err.printf ("Failed while reading bytes from %s: %s", url.toExternalForm(), e.getMessage());
e.printStackTrace ();
// Perform any other exception handling that's appropriate.
}
finally {
if (is != null) { is.close(); }
}
return baos.toByteArray():
I'm using custom TileProviders in my Android app to display offline maps and OpenStreetMap maps. It works, but there is a problem with the tiles resolution, which is quite bad. The files have a size of 256x256, and setting the width/height of my TileProvider to 128 doesn't change anything.
Here is some piece of code :
public class GenericUrlTileProvider extends UrlTileProvider {
// ---------------------------------------------------------------------------------------
// Private attributes :
private String _baseUrl;
// ---------------------------------------------------------------------------------------
// ---------------------------------------------------------------------------------------
// Constructor :
public GenericUrlTileProvider(int width, int height, String url) {
super(width, height);
this._baseUrl = url;
}
#Override
public URL getTileUrl(int x, int y, int zoom) {
try {
return new URL(_baseUrl.replace("{z}", "" + zoom).replace("{x}", "" + x).replace("{y}", "" + y));
}
catch (MalformedURLException e) { e.printStackTrace(); }
return null;
}
// ---------------------------------------------------------------------------------------
}
Does anyone know how to fix this to support high resolution devices ?
Thanks
On #grub request, here is what I did for getting the 4 tiles of the next zoom level :
public Tile getTileFromNextZoomLevel(int x, int y, int zoom) {
final String topLeftTileUrl = _source.getUrlSchema().replace("{z}", "" + (zoom + 1)).replace("{x}", "" + (x * 2)).replace("{y}", "" + (y * 2));
final String topRightTileUrl = _source.getUrlSchema().replace("{z}", "" + (zoom + 1)).replace("{x}", "" + (x * 2 + 1)).replace("{y}", "" + (y * 2));
final String bottomLeftTileUrl = _source.getUrlSchema().replace("{z}", "" + (zoom + 1)).replace("{x}", "" + (x * 2)).replace("{y}", "" + (y * 2 + 1));
final String bottomRightTileUrl = _source.getUrlSchema().replace("{z}", "" + (zoom + 1)).replace("{x}", "" + (x * 2 + 1)).replace("{y}", "" + (y * 2 + 1));
final Bitmap[] tiles = new Bitmap[4];
Thread t1 = new Thread() {
#Override
public void run() { tiles[0] = Utils.getBitmapFromURL(topLeftTileUrl); }
};
t1.start();
Thread t2 = new Thread() {
#Override
public void run() { tiles[1] = Utils.getBitmapFromURL(topRightTileUrl); }
};
t2.start();
Thread t3 = new Thread() {
#Override
public void run() { tiles[2] = Utils.getBitmapFromURL(bottomLeftTileUrl); }
};
t3.start();
Thread t4 = new Thread() {
#Override
public void run() { tiles[3] = Utils.getBitmapFromURL(bottomRightTileUrl); }
};
t4.start();
try {
t1.join();
t2.join();
t3.join();
t4.join();
}
catch (InterruptedException e) { e.printStackTrace(); }
byte[] tile = Utils.mergeBitmaps(tiles, Bitmap.CompressFormat.JPEG); // PNG is a lot slower, use it only if you really need to
return tile == null ? TileProvider.NO_TILE : new Tile( (int) _source.getTileSize().getWidth(), (int) _source.getTileSize().getHeight(), tile);
}
And the Utils methods :
public static byte[] mergeBitmaps(Bitmap[] parts, Bitmap.CompressFormat format) {
// Check if all the bitmap are null (if so return null) :
boolean allNulls = true;
for (int i = 0; i < parts.length; i++) {
if(parts[i] != null) {
allNulls = false;
break;
}
}
if(allNulls) return null;
Bitmap tileBitmap = Bitmap.createBitmap(512, 512, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(tileBitmap);
Paint paint = new Paint();
for (int i = 0; i < parts.length; i++) {
if(parts[i] == null) {
parts[i] = Bitmap.createBitmap(256, 256, Bitmap.Config.ARGB_8888);
}
canvas.drawBitmap(parts[i], parts[i].getWidth() * (i % 2), parts[i].getHeight() * (i / 2), paint);
}
ByteArrayOutputStream stream = new ByteArrayOutputStream();
tileBitmap.compress(format, 100, stream);
byte[] bytes = stream.toByteArray();
return bytes;
}
public static Bitmap getBitmapFromURL(String urlString) {
try {
// Ensure the file exists :
if(Utils.getResponseCode(urlString) != 200) return null;
URL url = new URL(urlString);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setDoInput(true);
connection.connect();
Bitmap bitmap = BitmapFactory.decodeStream(connection.getInputStream());
return bitmap;
}
catch (IOException e) { return null; }
}
You may have to adapt it for your needs. Please note that my app is still under development, and this code may need some tests / improvements.
i am new to android programming and i try to draw a polyline on google maps v2 android. the coordinates (more than 100) are stored in two txt-files (one txt for lat and one for lng) in asstes directory. i tryed to load the content of the files in a string but now i don't how to convert these to double for the polyline feature.
Double.parseDouble(contentdlat); won't work!
the coordinates in the txt's ar seperatet with a "," and looks like:
dlat.txt = 42.4630,42.4539
dlng.txt = -75.0572,-73.9737
UPDATE: now i use only one file instead of two.
coord_short.txt = 42.4630,-75.0572,42.4539,-73.9737
the old code is shown below:
//Add Polyline
ArrayList<LatLng> all=new ArrayList<LatLng>();
ArrayList<Double> lat1=new ArrayList<Double>();
ArrayList<Double> lon=new ArrayList<Double>();
AssetManager assetManager = getAssets();
// To load dlat text file
InputStream inputdlat;
try {
inputdlat = assetManager.open("dlat.txt");
int sizedlat = inputdlat.available();
byte[] bufferdlat = new byte[sizedlat];
inputdlat.read(bufferdlat);
inputdlat.close();
// byte buffer into a string
String contentdlat = new String(bufferdlat);
Toast.makeText(this, contentdlat, Toast.LENGTH_SHORT).show();
//String[] splitdlat = contentdlat.split(",");
}
catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// To load dlng text file
InputStream inputdlng;
try {
inputdlng = assetManager.open("dlng.txt");
int sizedlng = inputdlng.available();
byte[] bufferdlng = new byte[sizedlng];
inputdlng.read(bufferdlng);
inputdlng.close();
// byte buffer into a string
String contentdlng = new String(bufferdlng);
Toast.makeText(this, contentdlng, Toast.LENGTH_SHORT).show();
//String[] splitdlng = contentdlng.split(",");
}
catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
double dlat = Double.parseDouble(contentdlat);
double dlat = Double.parseDouble(contentdlng);
//double[] dlat = {42.4630,42.4539};
//double[] dlon = new double[]{-75.0572,-73.9737};
for(double n : dlat){
lat1.add(n);
}
for(double n : dlon){
lon.add(n);
}
for(int a=0;a<lat1.size();a++)
{
LatLng allLatLng= new LatLng(lat1.get(a),lon.get(a));
all.add(allLatLng);
}
Polyline polyline = map.addPolyline(new PolylineOptions()
.addAll(all)
.width(8)
.color(Color.GREEN));
It will be great if anybody can help me.
Ok with Piyush Gupta's help i changed the code to:
AssetManager assetManager = getAssets();
// To load coordinate text with hundreds of coordinates file like
InputStream input;
try {
input = assetManager.open("coord_short.txt");
int size = input.available();
byte[] buffer = new byte[size];
input.read(buffer);
input.close();
// byte buffer into a string
String content = new String(buffer);
String[] separated = content.split(",");
String latString = separated[0];
String longString = separated[1];
double coordlat = Double.parseDouble(latString);
double coordlon = Double.parseDouble(longString);
LatLng coordlocation = new LatLng(coordlat, coordlon);
Polyline polyline = map.addPolyline(new PolylineOptions()
.add(coordlocation)
.width(8)
.color(Color.GREEN));
}
catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
But now my Polyline is not drawn on the map.
whats now wrong?
InputStream is = getAssets().open("test.txt");
int size = is.available();
byte[] buffer = new byte[size]; //declare the size of the byte array with size of the file
is.read(buffer); //read file
is.close(); //close file
// Store text file data in the string variable
String str_data = new String(buffer);