How to prevent buildings from moving on Google Maps Android API - android

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";
}
}

Related

I got problem with Marker using googlemap on android

got this message and i do not know how to solve it.
i'm getting data from a device (latitude, longitude, device_name, altitude).
when trying to get the device in real time moving i got this Log:
W/System.err: java.lang.ClassCastException: android.graphics.drawable.VectorDrawable cannot be cast to android.graphics.drawable.BitmapDrawable
W/System.err: at com.gabontech.gprstrack.activities.CurrentePositionActivity$6.doInBackground(CurrentePositionActivity.java:254)
W/System.err: at com.gabontech.gprstrack.activities.CurrentePositionActivity$6.doInBackground(CurrentePositionActivity.java:242)
W/System.err: at android.os.AsyncTask$3.call(AsyncTask.java:378)
W/System.err: at java.util.concurrent.FutureTask.run(FutureTask.java:266)
W/System.err: at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:289)
W/System.err: at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
W/System.err: at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
W/System.err: at java.lang.Thread.run(Thread.java:919)
i don't understand why. is there any probleme with my marker? please help . Also the marker does not move on the map. There is a mistake somewhere!
here is me code:
public class CurrentePositionActivity extends DrawerBaseActivity implements OnMapReadyCallback {
ActivityCurrentePositionBinding activityCurrentePositionBinding;
private static final String TAG = "Mappoisition Activity";
private GoogleMap map;
private String stopTime;
private boolean isRefreshLoced = false;
private long lastRefreshTime;
private Timer timer;
private AsyncTask downloadingAsync;
private Marker m;
ImageButton zoomin,zoomout;
ImageButton map_layer_icon, maplayer;
RelativeLayout content_layout, nodata_layout;
private int autoZoomedTimes = 0;
private HashMap<Integer, Marker> deviceIdMarkers;
private HashMap<String, Device> markerIdDevices;
private HashMap<Integer, Polyline> deviceIdPolyline;
private HashMap<Integer, LatLng> deviceIdLastLatLng;
// private HashMap<Integer, Marker> deviceIdSmallMarkerInfo;
boolean isAutoZoomEnabled = false;
boolean isTrafficEnabled = true;
boolean isShowTailsEnabled = true;
boolean isShowGeofencesEnabled = true;
private boolean isMaptraficactived = true;
ApiInterface.GetGeofencesResult geofencesResult;
ArrayList<PolygonWithName> polygonsWithDetails = new ArrayList<>();
ArrayList<HistoryItem> historyItems;
ApiInterface.GetHistoryResult getHistoryResult;
ArrayList<HistoryItemCoord> historyItemCoords;
ArrayList<HistoryItemClass> historyItemClasses;
HistoryItem item;
Device device ;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
activityCurrentePositionBinding = ActivityCurrentePositionBinding.inflate(getLayoutInflater());
setContentView(activityCurrentePositionBinding.getRoot());
ButterKnife.bind(this);
//fin de declaration de boutons
deviceIdMarkers = new HashMap<>();
markerIdDevices = new HashMap<>();
deviceIdPolyline = new HashMap<>();
deviceIdLastLatLng = new HashMap<>();
zoomin = findViewById(R.id.zoom_in);
zoomout = findViewById(R.id.zoom_out);
SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager()
.findFragmentById(R.id.map);
if (mapFragment != null) {
mapFragment.getMapAsync(this);
}
item = new Gson().fromJson(getIntent().getStringExtra("item"), HistoryItem.class);
device = new Gson().fromJson(getIntent().getStringExtra("device"), Device.class);
zoomin.setOnClickListener(new View.OnClickListener()
{
#Override
public void onClick(View v)
{
map.animateCamera(CameraUpdateFactory.zoomIn());
}
});
zoomout.setOnClickListener(new View.OnClickListener()
{
#Override
public void onClick(View v)
{
map.animateCamera(CameraUpdateFactory.zoomOut());
}
});
}
#Override
protected void onResume()
{
super.onResume();
timer = new Timer();
timer.schedule(new TimerTask()
{
#Override
public void run()
{
runOnUiThread(() -> {
float timeleft = 10 - Math.round(System.currentTimeMillis() - lastRefreshTime) / 1000f;
if (System.currentTimeMillis() - lastRefreshTime >= 10 * 1000)
if (map != null)
refresh();
});
}
}, 0, 1000);
}
#Override
protected void onPause()
{
super.onPause();
try
{
timer.cancel();
timer.purge();
downloadingAsync.cancel(true);
} catch (Exception e)
{
e.printStackTrace();
}
}
#Override
public void onMapReady(#NonNull GoogleMap googleMap) {
map = googleMap;
refresh();
map.setMapType(GoogleMap.MAP_TYPE_HYBRID);
}
private void refresh(){
device = new Gson().fromJson(getIntent().getStringExtra("device"), Device.class);
LatLng geopoint = new LatLng(Double.valueOf(device.lat), Double.valueOf(device.lng));
if (isRefreshLoced)
return;
isRefreshLoced = true;
lastRefreshTime = System.currentTimeMillis();
Log.d(TAG, "success: loaded icons");
downloadingAsync = new AsyncTask<Void, Void, Void>() {
MarkerOptions marker = new MarkerOptions();
int deviceId = device.id;
#Override
protected Void doInBackground(Void... params)
{
int dp100 = Utils.dpToPx(CurrentePositionActivity.this, 50);
String server_base = (String) DataSaver.getInstance(CurrentePositionActivity.this).load("server_base");
// LatLng geopoint = new LatLng(Double.valueOf(device.lat), Double.valueOf(device.lng));
try
{
Drawable dr = getResources().getDrawable(R.drawable.ic_direction);
Bitmap bmp = ((BitmapDrawable) dr).getBitmap();
int srcWidth = bmp.getWidth();
int srcHeight = bmp.getHeight();
int maxWidth = Utils.dpToPx(CurrentePositionActivity.this, 40);
int maxHeight = Utils.dpToPx(CurrentePositionActivity.this, 40);
float ratio = Math.min((float) maxWidth / (float) srcWidth, (float) maxHeight / (float) srcHeight);
int dstWidth = (int) (srcWidth * ratio);
int dstHeight = (int) (srcHeight * ratio);
bmp = bmp.createScaledBitmap(bmp, dp100, dp100, true);
// marker
MarkerOptions m = new MarkerOptions();
m.position(geopoint);
m.icon(BitmapDescriptorFactory.fromBitmap(Bitmap.createScaledBitmap(bmp, dstWidth, dstHeight, true)));
// info windo
map.addMarker(m);
map.moveCamera(CameraUpdateFactory.newCameraPosition(CameraPosition.fromLatLngZoom(geopoint, 14)));
} catch (OutOfMemoryError outOfMemoryError)
{
Toast.makeText(CurrentePositionActivity.this, "votre balise" + device.name, Toast.LENGTH_LONG).show();
} catch (Exception e)
{
e.printStackTrace();
}
return null;
}
#Override
protected void onPostExecute(Void aVoid){
if (autoZoomedTimes < 1)
{
new Handler().postDelayed(new Runnable()
{
#Override
public void run()
{
runOnUiThread(new Runnable()
{
#Override
public void run()
{
if (marker != null)
{
try
{
LatLngBounds.Builder builder = new LatLngBounds.Builder();
builder.include(new LatLng(Double.valueOf(device.lat), Double.valueOf(device.lng)));
LatLngBounds bounds = builder.build();
// int padding = 0; // offset from edges of the map in pixels
CameraUpdate cu = CameraUpdateFactory.newLatLngBounds(bounds, Utils.dpToPx(CurrentePositionActivity.this, 50));
map.animateCamera(cu);
} catch (Exception e)
{
}
}
autoZoomedTimes++;
}
});
}
}, 50);
} else if (isAutoZoomEnabled)
{
try
{
LatLngBounds.Builder builder = new LatLngBounds.Builder();
builder.include(m.getPosition());
LatLngBounds bounds = builder.build();
// int padding = 0; // offset from edges of the map in pixels
CameraUpdate cu = CameraUpdateFactory.newLatLngBounds(bounds, Utils.dpToPx(CurrentePositionActivity.this, 50));
map.animateCamera(cu);
} catch (Exception ignored)
{
}
map.moveCamera(CameraUpdateFactory.newLatLngZoom(geopoint, 15));
autoZoomedTimes++;
}
Log.d(TAG, "onPostExecute: icons downloaded and added to map, total markers: " + geopoint);
Marker m;
Polyline polyline;
if (deviceIdMarkers.containsKey(deviceId))
{
Log.d("aa", "moving to" + marker.getPosition());
Objects.requireNonNull(deviceIdMarkers.get(deviceId)).setPosition(new LatLng(device.lat, device.lng));
m = deviceIdMarkers.get(deviceId);
polyline = deviceIdPolyline.get(deviceId);
} else
{
Log.d("aa", "putting new");
m = map.addMarker(new MarkerOptions().position(new LatLng(Double.valueOf(device.lat), Double.valueOf(device.lng))));
deviceIdMarkers.put(device.id, m);
polyline = map.addPolyline(new PolylineOptions());
deviceIdPolyline.put(device.id, polyline);
}
Device thatonedevice = null;
thatonedevice = device;
assert m != null;
markerIdDevices.put(m.getId(), thatonedevice);
// update marker rotation based on driving direction
if (thatonedevice != null && deviceIdLastLatLng.containsKey(deviceId))
{
double dirLat = thatonedevice.lat - Objects.requireNonNull(deviceIdLastLatLng.get(deviceId)).latitude;
double dirLng = thatonedevice.lng - Objects.requireNonNull(deviceIdLastLatLng.get(deviceId)).longitude;
m.setRotation((float) Math.toDegrees(Math.atan2(dirLng, dirLat)));
}
deviceIdLastLatLng.put(device.id, new LatLng(thatonedevice.lat, thatonedevice.lng));
List<LatLng> polylinePoints = new ArrayList<>();
for (TailItem item : thatonedevice.tail)
polylinePoints.add(new LatLng(Double.valueOf(item.lat), Double.valueOf(item.lng)));
polyline.setPoints(polylinePoints);
polyline.setWidth(Utils.dpToPx(CurrentePositionActivity.this, 2));
polyline.setColor(Color.parseColor(thatonedevice.device_data.tail_color));
map.setInfoWindowAdapter(new GoogleMap.InfoWindowAdapter()
{
#Override
public View getInfoWindow(Marker marker)
{
return null;
}
#Override
public View getInfoContents(final Marker marker)
{
synchronized (this)
{
}
final Device device = markerIdDevices.get(marker.getId());
if (device != null)
{
View view = getLayoutInflater().inflate(R.layout.layout_map_infowindow, null);
view.bringToFront();
view.findViewById(R.id.close).setOnClickListener(new View.OnClickListener()
{
#Override
public void onClick(View v)
{
marker.hideInfoWindow();
}
});
TextView device_name = (TextView) view.findViewById(R.id.device_name);
device_name.setText(device.name);
TextView altitude = (TextView) view.findViewById(R.id.altitude);
altitude.setText(String.valueOf(device.altitude) + " " + device.unit_of_altitude);
TextView time = (TextView) view.findViewById(R.id.time);
time.setText(device.time);
TextView stopTimeView = (TextView) view.findViewById(R.id.stopTime);
stopTimeView.setText(stopTime);
TextView speed = (TextView) view.findViewById(R.id.speed);
speed.setText(device.speed + " " + device.distance_unit_hour);
TextView address = (TextView) view.findViewById(R.id.address);
address.setText(device.address);
final ArrayList<Sensor> showableSensors = new ArrayList<>();
for (Sensor item : device.sensors)
if (item.show_in_popup > 0)
showableSensors.add(item);
ListView sensors_list = (ListView) view.findViewById(R.id.sensors_list);
sensors_list.setAdapter(new AwesomeAdapter<Sensor>(CurrentePositionActivity.this)
{
#Override
public int getCount()
{
return showableSensors.size();
}
#NonNull
#Override
public View getView(int position, View convertView, #NonNull ViewGroup parent)
{
if (convertView == null)
convertView = getLayoutInflater().inflate(R.layout.adapter_map_sensorslist, null);
Sensor item = showableSensors.get(position);
TextView name = (TextView) convertView.findViewById(R.id.name);
name.setText(item.name);
TextView value = (TextView) convertView.findViewById(R.id.value);
value.setText(item.value);
return convertView;
}
});
List<Address> addresses;
try
{
addresses = new Geocoder(CurrentePositionActivity.this).getFromLocation(device.lat, device.lng, 1);
if (addresses.size() > 0)
address.setText(addresses.get(0).getAddressLine(0));
} catch (IOException e)
{
e.printStackTrace();
}
return view;
}
return null;
}
});
map.setOnMarkerClickListener(new GoogleMap.OnMarkerClickListener()
{
#Override
public boolean onMarkerClick(final Marker marker)
{
int px = Utils.dpToPx(CurrentePositionActivity.this, 300);
map.setPadding(0, px, 0, 0);
stopTime = "...";
final Device device = markerIdDevices.get(marker.getId());
if (device != null)
{
API.getApiInterface(CurrentePositionActivity.this).deviceStopTime((String) DataSaver.getInstance(CurrentePositionActivity.this).load("api_key"), "en", device.id, new Callback<ApiInterface.DeviceStopTimeResult>()
{
#Override
public void success(ApiInterface.DeviceStopTimeResult result, Response response)
{
stopTime = result.time;
marker.showInfoWindow();
}
#Override
public void failure(RetrofitError retrofitError)
{
Toast.makeText(CurrentePositionActivity.this, R.string.errorHappened, Toast.LENGTH_SHORT).show();
}
});
}
return false;
}
});
map.setOnInfoWindowClickListener(new GoogleMap.OnInfoWindowClickListener()
{
#Override
public void onInfoWindowClick(Marker marker)
{
marker.hideInfoWindow();
}
});
map.setOnInfoWindowCloseListener(new GoogleMap.OnInfoWindowCloseListener()
{
#Override
public void onInfoWindowClose(Marker marker)
{
map.setPadding(0, 0, 0, 0);
}
});
// updateSmallMarkerData(allDevices);
isRefreshLoced = false;
}
}.execute();
}
private void putGeofenceNameMarkers(int textSize) {
for (Geofence geofence : geofencesResult.items.geofences)
{
if (geofence.active == 1)
{
String strText = geofence.name;
Paint.FontMetrics fm = new Paint.FontMetrics();
Paint paintText = new Paint();
paintText.setColor(Color.parseColor(geofence.polygon_color));
paintText.setTextAlign(Paint.Align.CENTER);
paintText.setTypeface(Typeface.create(Typeface.DEFAULT, Typeface.BOLD));
paintText.getFontMetrics(fm);
Rect rectText = new Rect();
paintText.getTextBounds(strText, 0, strText.length(),
rectText);
Bitmap.Config conf = Bitmap.Config.ARGB_8888;
Bitmap bmpText = Bitmap.createBitmap(rectText.width(),
rectText.height(), conf);
Canvas canvas = new Canvas(bmpText);
canvas.drawText(strText, canvas.getWidth() / 2,
canvas.getHeight() - rectText.bottom, paintText);
MarkerOptions markerOptions = new MarkerOptions()
.position(centroid(geofence.coordinatesList))
.icon(BitmapDescriptorFactory.fromBitmap(bmpText))
.anchor(0.5f, 1);
Marker marker = map.addMarker(markerOptions);
polygonsWithDetails.add(new PolygonWithName(map.addPolygon(new PolygonOptions()
.addAll(geofence.coordinatesList)
.strokeColor(Color.parseColor(geofence.polygon_color))
.fillColor(Color.parseColor("#59" + geofence.polygon_color.substring(1))))
, paintText, markerOptions, marker, geofence));
}
}
}
private LatLng centroid(List<LatLng> points)
{
double[] centroid = {0.0, 0.0};
for (int i = 0; i < points.size(); i++)
{
centroid[0] += points.get(i).latitude;
centroid[1] += points.get(i).longitude;
}
int totalPoints = points.size();
centroid[0] = centroid[0] / totalPoints;
centroid[1] = centroid[1] / totalPoints;
return new LatLng(centroid[0], centroid[1]);
}
}
Bitmap bmp = ((BitmapDrawable) dr).getBitmap();
dr is a VectorDrawable and cannot be cast to BitmapDrawable.
To get the bitmap of it, can refer to:
Getting Bitmap from vector drawable

Google Maps offline Mode on Android Application

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

Find the closest point on polygon to user location

I have an app that find the shortest distance between my user to a polygon.
I want to convert the polygon to Geofence to check the distance between the user to the area to give mor accurate information to the user.
how can I do that?
this is the MapsActivity
public class MapsActivity extends FragmentActivity implements OnMapReadyCallback, LocationListener, MinimumDistanceTask.GetMinimumDistanceListener {
private GoogleMap mMap;
private LocationManager manager;
private double lat, lng;
private KmlLayer layer;
private LatLng latLngTest;
private boolean contains = false;
private ArrayList<LatLng> outerBoundary;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_maps);
// Obtain the SupportMapFragment and get notified when the map is ready to be used.
SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager()
.findFragmentById(R.id.map);
mapFragment.getMapAsync(this);
manager = (LocationManager) getSystemService(LOCATION_SERVICE);
}
#Override
protected void onResume() {
super.onResume();
String provider = LocationManager.GPS_PROVIDER;
//take the user location every second
try {
manager.requestLocationUpdates(provider, 1000, 0, this);
}catch (SecurityException e){
}
}
#Override
public void onMapReady(GoogleMap googleMap) {
mMap = googleMap;
}
#Override
public void onLocationChanged(Location location) {
//clear map before create new location
mMap.clear();
try {
//load the kml file
layer = new KmlLayer(mMap, R.raw.polygon_layer, this);
layer.addLayerToMap();
} catch (IOException e) {
e.printStackTrace();
} catch (XmlPullParserException e) {
e.printStackTrace();
}
lat = location.getLatitude();
lng = location.getLongitude();
latLngTest = new LatLng(lat,lng);
// Add a marker in user location
LatLng userLocation = new LatLng(latLngTest.latitude, latLngTest.longitude);
mMap.addMarker(new MarkerOptions().position(userLocation).title("you are here"));
mMap.animateCamera(CameraUpdateFactory.newLatLngZoom(userLocation, 15));
//check if the user in the polygon
boolean inside = ifUserInside();
if(inside){
Toast.makeText(MapsActivity.this, "you are in the polygon", Toast.LENGTH_SHORT).show();
}else{
Toast.makeText(MapsActivity.this, "you are outside the polygon", Toast.LENGTH_SHORT).show();
//create the string address for the url
String address = "";
for (int i = 0; i < outerBoundary.size(); i++) {
address += (outerBoundary.get(i).toString() + "|");
address = address.replace("lat/lng:", "");
address = address.replace(" ", "");
address = address.replace("(", "");
address = address.replace(")", "");
}
MinimumDistanceTask task = new MinimumDistanceTask(this);
task.execute("https://maps.googleapis.com/maps/api/distancematrix/json?units=imperial&origins="+latLngTest.latitude+ "," + latLngTest.longitude
+ "&destinations=" + address + "&mode=walking");
}
}
#Override
public void getMinimumDistance(int closeLocation) {
//check if you get results properly
if(closeLocation != -1) {
GetDirection direction = new GetDirection();
direction.execute("https://maps.googleapis.com/maps/api/directions/json?origin=" + latLngTest.latitude + "," + latLngTest.longitude
+ "&destination=" + outerBoundary.get(closeLocation).latitude + "+" + outerBoundary.get(closeLocation).longitude);
}
}
#Override
public void onStatusChanged(String provider, int status, Bundle extras) {
}
#Override
public void onProviderEnabled(String provider) {
}
#Override
public void onProviderDisabled(String provider) {
}
public boolean ifUserInside(){
if (layer.getContainers() != null) {
for (KmlContainer container : layer.getContainers()) {
if (container.getPlacemarks() != null) {
for (KmlPlacemark placemark : container.getPlacemarks()) {
contains = false;
if (placemark.getGeometry() instanceof KmlPolygon) {
KmlPolygon polygon = (KmlPolygon) placemark.getGeometry();
// Get the outer boundary and check if the test location lies inside
outerBoundary = polygon.getOuterBoundaryCoordinates();
contains = PolyUtil.containsLocation(latLngTest, outerBoundary, true);
if (contains) {
// Get the inner boundaries and check if the test location lies inside
ArrayList<ArrayList<LatLng>> innerBoundaries = polygon.getInnerBoundaryCoordinates();
if (innerBoundaries != null) {
for (ArrayList<LatLng> innerBoundary : innerBoundaries) {
// If the test location lies in a hole, the polygon doesn't contain the location
if (PolyUtil.containsLocation(latLngTest, innerBoundary, true)) {
contains = false;
}
}
}
}
}
}
}
}
}
return contains;
}
public class GetDirection extends AsyncTask<String , Void, String> {
HttpsURLConnection connection = null;
BufferedReader reader = null;
StringBuilder builder = new StringBuilder();
#Override
protected String doInBackground(String... params) {
String address = params[0];
try {
URL url = new URL(address);
connection = (HttpsURLConnection) url.openConnection();
if(connection.getResponseCode() != HttpURLConnection.HTTP_OK){
return "Error from server";
}
reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
String line;
while ((line = reader.readLine()) != null){
builder.append(line);
}
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return builder.toString();
}
#Override
protected void onPostExecute(String s) {
//get the polyline string
String polygonPoints = "";
try {
JSONObject object = new JSONObject(s);
JSONArray array = object.getJSONArray("routes");
for (int i = 0; i < array.length(); i++) {
JSONObject arrObj1 = array.getJSONObject(i);
JSONObject points = arrObj1.getJSONObject("overview_polyline");
polygonPoints = points.getString("points");
}
//convert the string to polyline;
ArrayList<LatLng> a = new ArrayList<>(decodePolyPoints(polygonPoints));
//add polyline to the map
mMap.addPolyline(new PolylineOptions().addAll(a).width(10).color(Color.BLUE));
} catch (JSONException e) {
e.printStackTrace();
}
}
}
//the method that convert the string to polyline
public static ArrayList<LatLng> decodePolyPoints(String encodedPath){
int len = encodedPath.length();
final ArrayList<LatLng> path = new ArrayList<LatLng>();
int index = 0;
int lat = 0;
int lng = 0;
while (index < len) {
int result = 1;
int shift = 0;
int b;
do {
b = encodedPath.charAt(index++) - 63 - 1;
result += b << shift;
shift += 5;
} while (b >= 0x1f);
lat += (result & 1) != 0 ? ~(result >> 1) : (result >> 1);
result = 1;
shift = 0;
do {
b = encodedPath.charAt(index++) - 63 - 1;
result += b << shift;
shift += 5;
} while (b >= 0x1f);
lng += (result & 1) != 0 ? ~(result >> 1) : (result >> 1);
path.add(new LatLng(lat * 1e-5, lng * 1e-5));
}
return path;
}
}
This is my AsyncTask to get the minimum distance point
public class MinimumDistanceTask extends AsyncTask<String, Void, Integer>{
private int closeLocation;
// private String points;
private GetMinimumDistanceListener listener;
public MinimumDistanceTask(GetMinimumDistanceListener listener){
// this.points = points;
this.listener = listener;
}
#Override
protected Integer doInBackground(String... params) {
HttpsURLConnection connection = null;
BufferedReader reader = null;
StringBuilder builder = new StringBuilder();
int minimumDis = -1;
String address = params[0];
try {
URL url = new URL(address);
connection = (HttpsURLConnection) url.openConnection();
if(connection.getResponseCode() != HttpURLConnection.HTTP_OK){
return -1;
}
reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
String line;
while ((line = reader.readLine()) != null){
builder.append(line);
}
///get the json data
JSONObject jsonObject1 = new JSONObject(builder.toString());
JSONArray points = jsonObject1.getJSONArray("rows");
JSONObject jsonObject2 = points.getJSONObject(0);
JSONArray elements = jsonObject2.getJSONArray("elements");
for (int i = 0; i < elements.length(); i++) {
JSONObject jsonObject3 = elements.getJSONObject(i);
JSONObject distance = jsonObject3.getJSONObject("distance");
if( distance.getInt("value") < minimumDis || minimumDis == -1) {
minimumDis = distance.getInt("value");
closeLocation = i;
}
}
} catch (MalformedURLException | JSONException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return closeLocation;
}
#Override
protected void onPostExecute(Integer closeLocation) {
listener.getMinimumDistance(closeLocation);
}
public interface GetMinimumDistanceListener{
void getMinimumDistance(int closeLocation);
}
}
thanks a lot :)
You can use a function like the following to calculate the nearest point from polygon defined by a List<LatLng> and a given LatLng.
It uses the PolyUtil.distanceToLine from the Google Maps Android API Utility Library to compute the distance between the test LatLng and every segment of the list, and a method based on the distanceToLine method from https://github.com/googlemaps/android-maps-utils/blob/master/library/src/com/google/maps/android/PolyUtil.java to compute the projection of a point on a segment.
private LatLng findNearestPoint(LatLng test, List<LatLng> target) {
double distance = -1;
LatLng minimumDistancePoint = test;
if (test == null || target == null) {
return minimumDistancePoint;
}
for (int i = 0; i < target.size(); i++) {
LatLng point = target.get(i);
int segmentPoint = i + 1;
if (segmentPoint >= target.size()) {
segmentPoint = 0;
}
double currentDistance = PolyUtil.distanceToLine(test, point, target.get(segmentPoint));
if (distance == -1 || currentDistance < distance) {
distance = currentDistance;
minimumDistancePoint = findNearestPoint(test, point, target.get(segmentPoint));
}
}
return minimumDistancePoint;
}
/**
* Based on `distanceToLine` method from
* https://github.com/googlemaps/android-maps-utils/blob/master/library/src/com/google/maps/android/PolyUtil.java
*/
private LatLng findNearestPoint(final LatLng p, final LatLng start, final LatLng end) {
if (start.equals(end)) {
return start;
}
final double s0lat = Math.toRadians(p.latitude);
final double s0lng = Math.toRadians(p.longitude);
final double s1lat = Math.toRadians(start.latitude);
final double s1lng = Math.toRadians(start.longitude);
final double s2lat = Math.toRadians(end.latitude);
final double s2lng = Math.toRadians(end.longitude);
double s2s1lat = s2lat - s1lat;
double s2s1lng = s2lng - s1lng;
final double u = ((s0lat - s1lat) * s2s1lat + (s0lng - s1lng) * s2s1lng)
/ (s2s1lat * s2s1lat + s2s1lng * s2s1lng);
if (u <= 0) {
return start;
}
if (u >= 1) {
return end;
}
return new LatLng(start.latitude + (u * (end.latitude - start.latitude)),
start.longitude + (u * (end.longitude - start.longitude)));
}
You can test it with the following code:
List<LatLng> points = new ArrayList<>();
points.add(new LatLng(2, 2));
points.add(new LatLng(4, 2));
points.add(new LatLng(4, 4));
points.add(new LatLng(2, 4));
points.add(new LatLng(2, 2));
LatLng testPoint = new LatLng(3, 0);
LatLng nearestPoint = findNearestPoint(testPoint, points);
Log.e("NEAREST POINT: ", "" + nearestPoint); // lat/lng: (3.0,2.0)
Log.e("DISTANCE: ", "" + SphericalUtil.computeDistanceBetween(testPoint, nearestPoint)); // 222085.35856591124

Adding an Authorization header in getTileUrl for Maps Tile Android

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():

Google Maps API Android - Tile provider's tiles resolution

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.

Categories

Resources