I followed the example here: How to set screen zoom by geopoints?
But my problem is that my route is very small in the mapview. It should end near the borders.
This is how it looks with the code below:
private void centerMap(ArrayList<GeoPoint> waypoints) {
GeoPoint[] geoPoints = new GeoPoint[waypoints.size()];
for(int i = 0; i<waypoints.size(); i++) {
geoPoints[i] = waypoints.get(i);
}
int minLat = Integer.MAX_VALUE;
int maxLat = Integer.MIN_VALUE;
int minLon = Integer.MAX_VALUE;
int maxLon = Integer.MIN_VALUE;
//for (Point point : twoPoints) {
for(GeoPoint point: geoPoints){
int lat = (int) (point.getLatitude() * 1E6);
int lon = (int) (point.getLongitude() * 1E6);
maxLat = Math.max(lat, maxLat);
minLat = Math.min(lat, minLat);
maxLon = Math.max(lon, maxLon);
minLon = Math.min(lon, minLon);
}
mapView.getController().zoomToSpan(Math.abs(maxLat - minLat), Math.abs(maxLon - minLon));
mapView.getController().setCenter(new GeoPoint((maxLat + minLat) / 2, (maxLon + minLon) / 2));
}
private void centerMap(ArrayList<GeoPoint> waypoints) {
BoundingBox bb = BoundingBox.fromGeoPoints(waypoints);
mapView.zoomToBoundingBox(bb);
}
And note that lat/lon values are now double by default in osmdroid.
WMS Webservice GeoServer WMS
I try to get Tile Information(I, J , BBOX) on selected Latitude and Longitude with zooming level in Google Map.
I used this formula to get I, J , BBOX Formula Source
private void getXYFromLatLon(double lat, double lon, final int zoom) {
int tileSize = 256;
// double initialResolution = 2 * Math.PI * 6378137 / tileSize;
double initialResolution = 156543.03392804062;
double originShift = 20037508.342789244;
// LatLong to Meter
double mx = lon * originShift / 180.0;
double my = Math.log(Math.tan((90 + lat) * Math.PI / 360.0))
/ (Math.PI / 180.0);
my = my * originShift / 180.0;
// Meter to Pixels
double res = initialResolution / (2 * zoom);
double px = (mx + originShift) / res;
double py = (my + originShift) / res;
getBoundingBox(Double.valueOf(px).intValue(), Double.valueOf(py)
.intValue(), zoom);
// Pixel to tiles
final int tx = (int) Math.ceil(px / ((tileSize)) - 1);
final int ty = (int) Math.ceil(py / ((tileSize)) - 1);
getTileBound(tx, ty, zoom, tileSize);
Toast.makeText(getApplicationContext(), "X: " + tx + ",Y: " + ty,
Toast.LENGTH_SHORT).show();
}private void getTileBound(int tx, int ty, int zoom, int tileSize) {
double[] min = pixelToMeter(tx * tileSize, ty * tileSize, zoom);
double[] max = pixelToMeter((tx + 1) * tileSize, (ty + 1) * tileSize,
zoom);
builder.append("\nMIN-X:" + min[0]).append("\nMIN-Y:" + min[1])
.append("\nMAX-X:" + max[0]).append("\nMAX-Y:" + max[1])
.append("\nI:" + (tx)).append("\nJ:" + (ty));
((TextView) findViewById(R.id.textView1)).setText(builder.toString());
/*
* Toast.makeText(getApplicationContext(), "X: " + min.toString() +
* ",Y: " + max.toString(), Toast.LENGTH_SHORT).show();
*/
}public String getTileNumber(final double lat, final double lon,
final int zoom) {
int xtile = (int) Math.floor((lon + 180) / 360 * (1 << zoom));
int ytile = (int) Math
.floor((1 - Math.log(Math.tan(Math.toRadians(lat)) + 1
/ Math.cos(Math.toRadians(lat)))
/ Math.PI)
/ 2 * (1 << zoom));
if (xtile < 0)
xtile = 0;
if (xtile >= (1 << zoom))
xtile = ((1 << zoom) - 1);
if (ytile < 0)
ytile = 0;
if (ytile >= (1 << zoom))
ytile = ((1 << zoom) - 1);
System.out.println("xtile" + xtile);
// Toast.makeText(getApplicationContext(),
// xtile + "YY" + ytile + "Zoom" + (1 << zoom), Toast.LENGTH_LONG)
// .show();
return ("" + zoom + "/" + xtile + "/" + ytile);
}private double[] pixelToMeter(int x, int y, int zoom) {
int tileSize = 256;
double initialResolution = 2 * Math.PI * 6378137 / tileSize;
double originShift = 2 * Math.PI * 6378137 / 2;
double res = initialResolution / (2 * zoom);
double mx = x * res - originShift;
double my = y * res - originShift;
return new double[] { mx, my };
}
The problem based on zooming level i'm not able to find the exact value ..
Based on correct value i to have call the WMS webservices
Thanks in advance...
http://192.168.1.102:1005/geoserver/estater/wms?SERVICE=WMS&VERSION=1.3.0&REQUEST=GetFeatureInfo&FORMAT=image%2Fpng&TRANSPARENT=true&QUERY_LAYERS=buildings&LAYERS=kwt_buildings&INFO_FORMAT=application%2Fjson&propertyName=grid_id%2Cbuild_id&I=90&J=161&WIDTH=256&HEIGHT=256&CRS=EPSG%3A3857&STYLES=&BBOX=5342031.032794397%2C3420709.8898182083%2C5343254.02524696%2C3421932.882270771
I did not look at your code, but I have mine working since years, so here it is the WMS tile provider class:
public abstract class WMSTileProvider extends UrlTileProvider {
// Web Mercator n/w corner of the map.
private static final double[] TILE_ORIGIN = { -20037508.34789244, 20037508.34789244 };
// array indexes for that data
private static final int ORIG_X = 0;
private static final int ORIG_Y = 1; // "
// Size of square world map in meters, using WebMerc projection.
private static final double MAP_SIZE = 20037508.34789244 * 2;
// array indexes for array to hold bounding boxes.
protected static final int MINX = 0;
protected static final int MAXX = 1;
protected static final int MINY = 2;
protected static final int MAXY = 3;
// cql filters
private String cqlString = "";
// Construct with tile size in pixels, normally 256, see parent class.
public WMSTileProvider(int x, int y) {
super(x, y);
}
#SuppressWarnings("deprecation")
protected String getCql() {
try {
return URLEncoder.encode(cqlString, Charset.defaultCharset().name());
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
return URLEncoder.encode(cqlString);
}
}
public void setCql(String c) {
cqlString = c;
}
// Return a web Mercator bounding box given tile x/y indexes and a zoom
// level.
protected double[] getBoundingBox(int x, int y, int zoom) {
double tileSize = MAP_SIZE / Math.pow(2, zoom);
double minx = TILE_ORIGIN[ORIG_X] + x * tileSize;
double maxx = TILE_ORIGIN[ORIG_X] + (x + 1) * tileSize;
double miny = TILE_ORIGIN[ORIG_Y] - (y + 1) * tileSize;
double maxy = TILE_ORIGIN[ORIG_Y] - y * tileSize;
double[] bbox = new double[4];
bbox[MINX] = minx;
bbox[MINY] = miny;
bbox[MAXX] = maxx;
bbox[MAXY] = maxy;
return bbox;
}
}
And here is something on how i use it:
public static WMSTileProvider getWMSTileProviderByName(String layerName) {
final String OSGEO_WMS = "http://yourserver/geoserver/gwc/service/wms/?"
+ "LAYERS=" + layerName
+ "&FORMAT=image/png8&"
+ "PROJECTION=EPSG:3857&"
+ "TILEORIGIN=lon=-20037508.34,lat=-20037508.34&"
+ "TILESIZE=w=256,h=256"
+ "&MAXEXTENT=-20037508.34,-20037508.34,20037508.34,20037508.34&SERVICE=WMS&VERSION=1.1.1&REQUEST=GetMap&STYLES=&SRS=EPSG:3857"
+ "&BBOX=%f,%f,%f,%f&WIDTH=256&HEIGHT=256";
return new WMSTileProvider(256, 256) {
#Override
public synchronized URL getTileUrl(int x, int y, int zoom) {
final double[] bbox = getBoundingBox(x, y, zoom);
String s = String.format(Locale.US, OSGEO_WMS, bbox[MINX], bbox[MINY], bbox[MAXX], bbox[MAXY]);
try {
return new URL(s);
} catch (MalformedURLException e) {
throw new AssertionError(e);
}
}
};
}
Can't give you more code, but I hope can help you
EDIT: Given the comment, now i see what you need.
Here it is some code (old but working) on which you have to work a bit, it was a sort of hack:
private static final double[] TILES_ORIGIN = {-20037508.34789244, 20037508.34789244};//TODO Duplicate from WMS PROVIDER, put as utils
// Size of square world map in meters, using WebMerc projection.
private static final double MAP_SIZE = 20037508.34789244 * 2;//TODO Duplicate from WMS PROVIDER, put as utils
private static final double ORIGIN_SHIFT = Math.PI * 6378137d;
/**
* Transform the y map meter in y cordinate
*
* #param latitude the latitude of map
* #return meters of y cordinate
*/
private double inMetersYCoordinate(double latitude) {
if (latitude < 0) {
return -inMetersYCoordinate(-latitude);
}
return (Math.log(Math.tan((90d + latitude) * Math.PI / 360d)) / (Math.PI / 180d)) * ORIGIN_SHIFT / 180d;
}
/**
* Transform the x map meter in x cordinate
*
* #param longitude the longitude of map
* #return meters of x cordinate
*/
private double inMetersXCoordinate(double longitude) {
return longitude * ORIGIN_SHIFT / 180.0;
}
/**
* Get the Tile from x and y cordinates
*
* #param pointX x of the map
* #param pointY y of the map
* #param zoomLevel zoom of Tile
* #return the relative TileDataInfo
*/
private TileDataInfo getTileByCoordinate(double pointX, double pointY, int zoomLevel) {
final double tileDim = MAP_SIZE / Math.pow(2d, zoomLevel);
final int tileX = (int) ((pointX - TILES_ORIGIN[0]) / tileDim);
final int tileY = (int) ((TILES_ORIGIN[1] - pointY) / tileDim);
return new TileDataInfo(tileX, tileY, zoomLevel);
}
private static class TileDataInfo {
int tileX;
int tileY;
int tileZoom;
public TileDataInfo(int tileX, int tileY, int tileZoom) {
this.tileX = tileX;
this.tileY = tileY;
this.tileZoom = tileZoom;
}
}
In order to get the code right, you have to convert latitude in meters using the "inMetersYCoordinate", the longitude using "inMetersXCoordinate" and then use "getTileByCoordinate" to calculate the tile x,y,z (i,j,zoom for you)
I'm using this code to convert Google Map coordinates into an X,Y system on a custom overlay. This takes a latitude, longitude, and a zoom and converts it to x,y with a custom center point built in. What are the correct steps to have an x,y and convert it back to latitude and longitude.
static Double game_1_x = 1972.606;
static Double game_1_y = 3817.044;
static Double map_1_lng = 42.012002;
static Double map_1_lat = 42.850185;
static Double game_2_x = -1210.765;
static Double game_2_y = -3443.753;
static Double map_2_lng = -49.922088;
static Double map_2_lat = -83.293854;
public static String convertToXY(String lat, String lon, float zoom) {
int mapSize = 0;
if (zoom == 2.0f) {
mapSize = 1024;
} else if (zoom == 3.0f) {
mapSize = 2048;
} else if (zoom == 4.0f) {
mapSize = 4096;
} else if (zoom == 5.0f) {
mapSize = 8192;
}
Double LAT = Double.valueOf(lat);
Double LON = Double.valueOf(lon);
// get marker x value
Double markerLon = (LON + 180) * (mapSize / 360);
// convert Lat to Radians
Double markerLatRad = LAT * Math.PI / 180;
// get marker y value
Double mercN = Math.log(Math.tan((Math.PI / 4) + (markerLatRad / 2)));
Double markerLat = (mapSize / 2) - (mapSize * mercN / (2 * Math.PI));
// get map 1 x value
Double m1lng = (map_1_lng + 180) * (mapSize / 360);
// get map 2 x value
Double m2lng = (map_2_lng + 180) * (mapSize / 360);
// convert Lat to Radians
Double m1LatRad = map_1_lat * Math.PI / 180;
Double m2LatRad = map_2_lat * Math.PI / 180;
// get map 1 y value
Double mercNm1y = Math.log(Math.tan((Math.PI / 4) + (m1LatRad / 2)));
Double m1lat = (mapSize / 2) - (mapSize * mercNm1y / (2 * Math.PI));
// get map 2 y value
Double mercNm2y = Math.log(Math.tan((Math.PI / 4) + (m2LatRad / 2)));
Double m2lat = (mapSize / 2) - (mapSize * mercNm2y / (2 * Math.PI));
Double X = game_1_x + (markerLon - m1lng) * (game_1_x - game_2_x) / (m1lng - m2lng);
Double Y = game_1_y + (markerLat - m1lat) * (game_1_y - game_2_y) / (m1lat - m2lat);
return String.valueOf(X) + "," + String.valueOf(Y);
}
See if this is what you need:
mMap = mMapView.getMap();
mMap.getProjection().fromScreenLocation(Point p)
and the other way around
mMap.getProjection().toScreenLocation(LatLng l)
I am trying to add some MapOverlays to my MapView and I get the following Error:
java.util.ConcurrentModificationException
at java.util.ArrayList$ArrayListIterator.next(ArrayList.java:569)
at com.google.android.maps.OverlayBundle.draw(OverlayBundle.java:44)
at com.google.android.maps.MapView.onDraw(MapView.java:530)
at android.view.View.draw(View.java:13458)
...
at dalvik.system.NativeStart.main(Native Method)
This is my doInBackground() method of my AsyncTask:
Drawable marker = myContext.getResources().getDrawable(
R.drawable.marker);
Bitmap bitmap = ((BitmapDrawable) marker).getBitmap();
Drawable marker_new = new BitmapDrawable(myContext.getResources(),
Bitmap.createScaledBitmap(
bitmap, MainActivity.MARKER_SIZE * 10,
MainActivity.MARKER_SIZE * 10, true));
mapOverlays = map.getOverlays();
int minLat = Integer.MAX_VALUE;
int maxLat = Integer.MIN_VALUE;
int minLon = Integer.MAX_VALUE;
int maxLon = Integer.MIN_VALUE;
double fitFactor = 1.1;
MainActivity.mapListAddress.clear();
MainActivity.mapListTitle.clear();
MainActivity.mapListLati.clear();
MainActivity.mapListLongi.clear();
MainActivity.parseMaps();
for (int i = 0; i < MainActivity.mapListAddress.size(); i++) {
// String pointAddress = MainActivity.mapListAddress.get(i);
String pointTitel = MainActivity.mapListTitle.get(i);
MyItemizedOverlay itemizedOverlay = new MyItemizedOverlay(
marker_new, map);
double latitude = MainActivity.mapListLati.get(i) * 1e6;
double longitude = MainActivity.mapListLongi.get(i) * 1e6;
Geocoder geocoder;
List<Address> addresses = null;
geocoder = new Geocoder(Map.this, Locale.getDefault());
try {
addresses = geocoder.getFromLocation(MainActivity.mapListLati.get(i),
MainActivity.mapListLongi.get(i), 1);
} catch (IOException e) {
e.printStackTrace();
}
String address = addresses.get(0).getAddressLine(0);
String city = addresses.get(0).getAddressLine(1);
String country = addresses.get(0).getAddressLine(2);
String pointAddress = address + "\n" + city + "\n" + country;
GeoPoint p = new GeoPoint((int) latitude, (int) longitude);
overlayitem = new OverlayItem(p, pointTitel,
pointAddress);
itemizedOverlay.setBalloonBottomOffset(40);
itemizedOverlay.addOverlay(overlayitem);
mapOverlays.add(itemizedOverlay);
int lat = p.getLatitudeE6();
int lon = p.getLongitudeE6();
maxLat = Math.max(lat, maxLat);
minLat = Math.min(lat, minLat);
maxLon = Math.max(lon, maxLon);
minLon = Math.min(lon, minLon);
map.getController().zoomToSpan((int) (Math.abs(maxLat - minLat) * fitFactor),
(int) (Math.abs(maxLon - minLon) * fitFactor));
map.getController().animateTo(new GeoPoint((maxLat + minLat) / 2,
(maxLon + minLon) / 2));
}
return null;
Why does this error occur and what am I doing wrong in my code?
Try to synchronize the mapOverlays.add(itemizedOverlay);. I think this is where the issue is, multiple threads trying to add at the same time.
I have an array of latitude and longitude points. I use them to make markers on the map. What can I do to show all of the markers on the map at the same time. eg make a good fit so that if the markers are within a city I zoom it in to only show the city and not the whole state, country etc...
Thanks
something like this might help
private void centerGroup(int groupId){
if(groupId>0){
int minLat = Integer.MAX_VALUE;
int maxLat = Integer.MIN_VALUE;
int minLon = Integer.MAX_VALUE;
int maxLon = Integer.MIN_VALUE;
Cursor cursor = getYourLantLongs(groupId);
if (cursor.moveToFirst()){
final int LAT_INDEX = cursor.getColumnIndex(T.Waypoints.LATITUDE);
final int LON_INDEX = cursor.getColumnIndex(T.Waypoints.LONGITUDE);
do {
final int lat = (int) (cursor.getFloat(LAT_INDEX)*1E6);
final int lon = (int) (cursor.getFloat(LON_INDEX)*1E6);
maxLat = Math.max(lat, maxLat);
minLat = Math.min(lat, minLat);
maxLon = Math.max(lon, maxLon);
minLon = Math.min(lon, minLon);
} while(cursor.moveToNext());
mapController.zoomToSpan(Math.abs(maxLat - minLat), Math.abs(maxLon - minLon));
mapController.animateTo(new GeoPoint((maxLat + minLat)/2, (maxLon + minLon)/2 ));
/*
final int cLat = (int)((maxLat*1E6 + minLat*1E6)/2);
final int cLon = (int)((maxLon*1E6 + minLon*1E6)/2);
final int zLat = (int)Math.abs(maxLat - minLat);
final int zLon = (int)Math.abs(maxLon - minLon);
//mapController.zoomToSpan(zLat, zLon);
mapController.animateTo(new GeoPoint(cLat, cLon));
*/
}
}
}
in short you make a square and zoom in.
Go through your array and save min and max values of latitude and longitude.
Then use
mMapController.zoomToSpan((maxLatitude - minLatitude), (maxLongitude - minLongitude));
to get the right zoom level. And
mMapController.animateTo(new GeoPoint(
(maxLatitude + minLatitude)/2 ,
(maxLongitude + minLongitude)/2 ));
to move the map to the center of your markers.
Have fun!