Google android mapview lat long to topixel projection issue - android

I am getting different x,y value for a specific lat,long. I wonder why it is happening. May be I lack knowledge. So, if you anyone can explain by observing the following code snippet then it would be a great help.
import java.util.List;
import com.google.android.maps.GeoPoint;
import com.google.android.maps.MapActivity;
import com.google.android.maps.MapView;
import com.google.android.maps.Overlay;
import com.google.android.maps.OverlayItem;
import com.google.android.maps.Projection;
import android.app.Activity;
import android.graphics.Color;
import android.graphics.Point;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.widget.Button;
import android.widget.TextView;
public class MaopaoActivity extends MapActivity {
MapView FMmapview;
GeoPoint fm_gp;
Button bt;
int lat,lng;
List<Overlay> mapOverlays;
Drawable drawable;
CustomItemizedOverlay itemizedoverlay;
OverlayItem overlayitem;
String RASTA="";
String [] pairs;
int EndLat,EndLng;
String kotha;
TextView tv;
double startlat,startlong;
double endlat,endlong;
double midlat,midlong;
int startlatE6,startlongE6;
int endlatE6,endlongE6;
int midlatE6,midlongE6;
GeoPoint StartGp,EndGp,midGp1,midGp2;
Integer x1,y1,x2,y2,x3,y3;
Double x4,y4;
Point StartPoint;
Point EndPoint;
Point MidPoint1;
Point MidPoint2;
String abc;
double w,T,G;
Integer Tint,Gint;
#Override
protected void onCreate(Bundle icicle)
{
// TODO Auto-generated method stub
super.onCreate(icicle);
setContentView(R.layout.fullmap);
StartPoint = new Point();
EndPoint = new Point();
MidPoint1 = new Point();
MidPoint2 = new Point();
tv=(TextView)findViewById(R.id.fm_tv1);
startlat=34.123018;
startlatE6=(int)(startlat*1E6);
startlong=-118.146278;
startlongE6=(int)(startlong*1E6);
endlat=34.122525;
endlatE6=(int)(endlat*1000000);
endlong=-118.146241;
endlongE6=(int)(endlong*1000000);
midlat=34.122724;
midlatE6=(int)(midlat*1000000);
midlong=-118.146106;
midlongE6=(int)(midlong*1000000);
EndGp=new GeoPoint(endlatE6,endlongE6);
midGp1=new GeoPoint(midlatE6,midlongE6);
StartGp=new GeoPoint(startlatE6,startlongE6);
FMmapview=(MapView)findViewById(R.id.MapViewFM123);
Projection projection = FMmapview.getProjection();
FMmapview.getController().setCenter(StartGp);
FMmapview.getController().setZoom(18);
FMmapview.getProjection().toPixels(EndGp,EndPoint);
FMmapview.getProjection().toPixels(midGp1,MidPoint1);
FMmapview.getProjection().toPixels(StartGp,StartPoint);
x1=StartPoint.x;
y1=StartPoint.y;
x2=EndPoint.x;
y2=EndPoint.y;
x3=MidPoint1.x;
y3=MidPoint1.y;
w=(y2-y1)/(x2-x1);
T=((x3+(w*w*x1)+(w*y3)-(w*x1))/(w*w+1));
G=w*(T-x1)+y1;
Tint=(int) Math.ceil(T);
Gint=(int)Math.ceil(G);
mapOverlays = FMmapview.getOverlays();
drawable = this.getResources().getDrawable(R.drawable.blue);
itemizedoverlay = new CustomItemizedOverlay(drawable,this);
overlayitem = new OverlayItem(midGp1, "Your are at","hello motttto");
itemizedoverlay.addOverlay(overlayitem);
mapOverlays.add(itemizedoverlay);
midGp2=FMmapview.getProjection().fromPixels(Tint,Gint);
//x4=(midGp2.getLatitudeE6())/(1E6);
//y4=(midGp2.getLongitudeE6())/(1E6);
abc="A="+x1.toString()+','+y1.toString()+" B="+x2.toString()+","+y2.toString()+" C="+x3.toString()+","+y3.toString()+" D="+Tint.toString()+","+Gint.toString();
tv.setText(abc);
//midGp2=new GeoPoint(34048039,-118140655);
mapOverlays = FMmapview.getOverlays();
drawable = this.getResources().getDrawable(R.drawable.blue);
itemizedoverlay = new CustomItemizedOverlay(drawable,this);
overlayitem = new OverlayItem(midGp2, "Your are at","hello motttto");
itemizedoverlay.addOverlay(overlayitem);
mapOverlays.add(itemizedoverlay);
FMmapview.getController().animateTo(StartGp);
FMmapview.getController().setCenter(StartGp);
FMmapview.getController().setZoom(18);
FMmapview.getOverlays().add(new MyOverLay(StartGp,EndGp,2,Color.BLUE));
}
#Override
protected boolean isRouteDisplayed()
{
// TODO Auto-generated method stub
return false;
}
}
Here I get x,y=0,0 for the geopoint 34.123018,-118.146278;
and again I get x,y=240,340 when I re-run the app.
If I clear data, I get x,y=0,0
and then When I re-run I get different value for the same aforementioned lat/long.
It would be a great help if anyone can clear my conception
EDITED:
I have found the reason, which is totally unusual to me.
The projection value to pixel may vary.
as http://code.google.com/android/add-ons/google-apis/reference/com/google/android/maps/MapView.html#getProjection%28%29 says that
"The Projection of the map in its current state. You should not hold on to this object for more than one draw, since the projection of the map could change."
So, x,y can be 0,0 or can be anything. Whatever might be the projected x,y value is, it will always represent same lat,long.
The problem that leads me to such thinking unfortunately was the following line:
T=((x3+(w*w*x1)+(w*y3)-(w*x1))/(w*w+1));
It provides different value. If I break this into 3 lines then T provides correct result. Though, I never had to face such situation in C++. This is something weird.
Anyway, I would still want to know the reason behind the value of the projection being different.

I believe the answer to your question is that the zoom function in MapView is animated, that means it doesn’t complete immediately. So, the different projections you are getting results from them being taken during the zoom animation where the locations in the map are changing at the same time you are taking them.
To confirm that, you can make a delay of a couple of seconds between the map center/zoom change and taking the projections and you should always get the same projection values.
Good luck,
Luis

Related

Drawing a rectangle on the whole map with Polygon

I have been trying to draw a rectangle on the whole map(in a map activity) in Android Studio, i need a delimitation of the equator area, from one part of the map to the other. ( in a big rectangle) But every time i put the coordinates for the rectangle it goes the other way around, so it goes backwards and makes a small square from Pacific ocean to China , Australia and back.
Also, any idea how i can make a button take the shape of a country on the map?
package com.example.android.coffeeknowledge;
import android.content.res.Resources;
import android.support.v4.app.FragmentActivity;
import android.os.Bundle;
import android.util.Log;
import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.OnMapReadyCallback;
import com.google.android.gms.maps.SupportMapFragment;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.MapStyleOptions;
import com.google.android.gms.maps.model.MarkerOptions;
import com.google.android.gms.maps.model.Polygon;
import com.google.android.gms.maps.model.PolygonOptions;
import com.google.android.gms.maps.model.PolylineOptions;
public class coffeeMap extends FragmentActivity implements OnMapReadyCallback {
private GoogleMap mMap;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_coffee_map);
// 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);
}
/**
* Manipulates the map once available.
* This callback is triggered when the map is ready to be used.
* This is where we can add markers or lines, add listeners or move the camera. In this case,
* we just add a marker near Sydney, Australia.
* If Google Play services is not installed on the device, the user will be prompted to install
* it inside the SupportMapFragment. This method will only be triggered once the user has
* installed Google Play services and returned to the app.
*/
private static final LatLng cameraZoom = new LatLng(37.35, -122.0);
#Override
public void onMapReady(GoogleMap googleMap) {
try{
boolean success = googleMap.setMapStyle(
MapStyleOptions.loadRawResourceStyle(this, R.raw.mapstyle));
if(!success){
Log.e("coffeeMap","Style parsing failed.");
}
}catch(Resources.NotFoundException e){
Log.e("coffeeMap", "Can`t find style.Error: " , e);
}
mMap = googleMap;
// Instantiates a new Polygon object and adds points to define a rectangle
PolygonOptions rectOptions = new PolygonOptions()
.fillColor(R.color.white)
.add(new LatLng(24.376368, 101.181309),
new LatLng(-28.912738, 103.818027),
new LatLng(-26.841671, -117.944509),
new LatLng(27.616242, -122.020003),
new LatLng(24.376368, 101.181309));
// Get back the mutable Polygon
Polygon polygon = mMap.addPolygon(rectOptions);
// Add a marker in Sydney and move the camera
//mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(cameraZoom, 13));
LatLng sydney = new LatLng(35.175321, -107.619365);
mMap.addMarker(new MarkerOptions().position(sydney).title("Marker"));
}
}
Thank you.
The map api will always choose the shortest route between 2 points taking into account geodesic path or rectangular projection. The segments of interest in your example are:
new LatLng(-28.912738, 103.818027), // A
new LatLng(-26.841671, -117.944509), // B
and
new LatLng(27.616242, -122.020003), // C
new LatLng(24.376368, 101.181309)) // D
These two segments will cross the anti-meridian (and not go the other way around) because that is the shortest path between those two points. (This is always what would be desired.)
So to overcome this in your example, simply add an intermediate mid-point (assuming non-geodesic, or rectangular projection) in the segments (A-B and C-D) to force it to go "the other way". Using A-B as an example:
new LatLng(-28.912738, 103.818027),
new LatLng(-27.877204, -7.0), // approximate midpoint in desired direction (M)
new LatLng(-26.841671, -117.944509),
So the original distance from A-B (assuming geodesic) is 12830 km. And with the forced intermediate point is: A-M 10320km and M-B 10460km. These distance calculations are merely to demonstrate the point (pun intended).
The same approach applies to C-D.
So in pictures, your OP view using:
PolygonOptions rectOptions = new PolygonOptions()
.fillColor(R.color.colorPrimary)
.add(new LatLng(24.376368, 101.181309),
new LatLng(-28.912738, 103.818027),
new LatLng(-26.841671, -117.944509),
new LatLng(27.616242, -122.020003),
new LatLng(24.376368, 101.181309));
is displayed as:
and with the 2 intermediate points:
PolygonOptions rectOptions = new PolygonOptions()
.fillColor(R.color.colorPrimary)
.add(new LatLng(24.376368, 101.181309),
new LatLng(-28.912738, 103.818027),
new LatLng(-27.877204, -7.0),
new LatLng(-26.841671, -117.944509),
new LatLng(27.616242, -122.020003),
new LatLng( 25.9, -7.0),
new LatLng(24.376368, 101.181309));
is displayed as:
Just for fun, and to emphasize the midpoint determination depends on the projection, here is the same polygon using geodesic:
Finding the spherical midpoint for 2 points spanning an arc greater than pi radians is a problem for another day...
A handy online tool for more consideration: https://www.movable-type.co.uk/scripts/latlong.html.

To create map link in android [closed]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
This question appears to be off-topic because it lacks sufficient information to diagnose the problem. Describe your problem in more detail or include a minimal example in the question itself.
Closed 8 years ago.
Improve this question
I want to generate map link and want send mail of that link. when user will click on that link map should be opened in the browser with that location.I have generated the link and also it shows the location but problem is it does not shows the pin for current location.How to modify the link so that i can get the pin on map. The link which i have generated is as follows,
https://www.google.com/maps/preview/#"+latitude+","+longitude+",16z
http://www.openstreetmap.org/?mlat=latitude&mlon=longitude#map=zoom/latitude/longitude
where mlat and mlon specifiy the position of the marker.
Create a GIF image containing a pushpin (called pushpin) and copy it into the res/drawable-mdpi
folder of the existing project
import java.util.List;
import com.google.android.maps.GeoPoint;
import com.google.android.maps.MapActivity;
import com.google.android.maps.MapController;
import com.google.android.maps.MapView;
import com.google.android.maps.Overlay;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Point;
import android.os.Bundle;
import android.view.KeyEvent;
public classLBSActivity extendsMapActivity {
MapView mapView;
MapController mc;
GeoPoint p;
private classMapOverlay extendscom.google.android.maps.Overlay
{
#Override
public booleandraw(Canvas canvas, MapView mapView,
booleanshadow, longwhen)
{
super.draw(canvas, mapView, shadow);
//---translate the GeoPoint to screen pixels---Point screenPts = newPoint();
mapView.getProjection().toPixels(p, screenPts);
//---add the marker---FIGURE 9-10
Displaying Maps
❘ 367
Bitmap bmp = BitmapFactory.decodeResource(
getResources(), R.drawable.pushpin);
canvas.drawBitmap(bmp, screenPts.x, screenPts.y-50, null);
return true;
}
}
/** Called when the activity is first created. */
#Override
public voidonCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mapView= (MapView) findViewById(R.id.mapView);
mapView.setBuiltInZoomControls(true);
mapView.setSatellite(true);
mapView.setTraffic(true);
mc= mapView.getController();
String coordinates[] = {“1.352566007”, “103.78921587”};
doublelat = Double.parseDouble(coordinates[0]);
doublelng = Double.parseDouble(coordinates[1]);
p= newGeoPoint(
(int) (lat * 1E6),
(int) (lng * 1E6));
mc.animateTo(p);
mc.setZoom(13);
//---Add a location marker---MapOverlay mapOverlay = newMapOverlay();
List<Overlay> listOfOverlays = mapView.getOverlays();
listOfOverlays.clear();
listOfOverlays.add(mapOverlay);
mapView.invalidate();
}
public booleanonKeyDown(intkeyCode, KeyEvent event)
{
//...
}
#Override
protected booleanisRouteDisplayed() {
//...
}
}

Random markers and animation in Nutiteq 3D and OpenStreetMaps

I'm going to develop a location-based game for android platforms. I want to create a marker in random coordinates (around the user location) but on the road! Moreover i'm going to animate this marker from this point (A) to second random point (B). Any idea?
thank you!
ps.I use the sample code of the example of Nutiteq 3D map app.
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import com.nutiteq.MapView;
import com.nutiteq.components.Color;
import com.nutiteq.components.Components;
import com.nutiteq.components.MapPos;
import com.nutiteq.components.Options;
import com.nutiteq.geometry.Marker;
import com.nutiteq.log.Log;
import com.nutiteq.projections.EPSG3857;
import com.nutiteq.projections.Projection;
import com.nutiteq.rasterlayers.TMSMapLayer;
import com.nutiteq.style.MarkerStyle;
import com.nutiteq.ui.DefaultLabel;
import com.nutiteq.ui.Label;
import com.nutiteq.utils.UnscaledBitmapLoader;
import com.nutiteq.vectorlayers.MarkerLayer;
public class MainActivity extends Activity {
private MapView mapView;
#SuppressLint("NewApi")
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// enable logging for troubleshooting - optional
Log.enableAll();
Log.setTag("hellomap");
// 1. Get the MapView from the Layout xml - mandatory
mapView = (MapView) findViewById(R.id.mapView);
// Optional, but very useful: restore map state during device rotation,
// it is saved in onRetainNonConfigurationInstance() below
Components retainObject = (Components) getLastNonConfigurationInstance();
if (retainObject != null) {
// just restore configuration and update listener, skip other initializations
mapView.setComponents(retainObject);
MyLocationMapEventListener mapListener = (MyLocationMapEventListener) mapView.getOptions().getMapListener();
mapListener.reset(this, mapView);
mapView.startMapping();
return;
} else {
// 2. create and set MapView components - mandatory
mapView.setComponents(new Components());
}
// 3. Define map layer for basemap - mandatory.
// Here we use MapQuest open tiles
// Almost all online tiled maps use EPSG3857 projection.
TMSMapLayer mapLayer = new TMSMapLayer(new EPSG3857(), 0, 18, 0,
"http://otile1.mqcdn.com/tiles/1.0.0/osm/", "/", ".png");
mapView.getLayers().setBaseLayer(mapLayer);
// set initial map view camera - optional. "World view" is default
// Location: San Francisco
// NB! it must be in base layer projection (EPSG3857), so we convert it from lat and long
mapView.setFocusPoint(mapView.getLayers().getBaseLayer().getProjection().fromWgs84(-122.41666666667f, 37.76666666666f));
// rotation - 0 = north-up
mapView.setRotation(0f);
// zoom - 0 = world, like on most web maps
mapView.setZoom(10.0f);
// tilt means perspective view. Default is 90 degrees for "normal" 2D map view, minimum allowed is 30 degrees.
mapView.setTilt(75.0f);
// Activate some mapview options to make it smoother - optional
mapView.getOptions().setPreloading(true);
mapView.getOptions().setSeamlessHorizontalPan(true);
mapView.getOptions().setTileFading(true);
mapView.getOptions().setKineticPanning(true);
mapView.getOptions().setDoubleClickZoomIn(true);
mapView.getOptions().setDualClickZoomOut(true);
// set sky bitmap - optional, default - white
mapView.getOptions().setSkyDrawMode(Options.DRAW_BITMAP);
mapView.getOptions().setSkyOffset(4.86f);
mapView.getOptions().setSkyBitmap(
UnscaledBitmapLoader.decodeResource(getResources(),
R.drawable.sky_small));
// Map background, visible if no map tiles loaded - optional, default - white
mapView.getOptions().setBackgroundPlaneDrawMode(Options.DRAW_BITMAP);
mapView.getOptions().setBackgroundPlaneBitmap(
UnscaledBitmapLoader.decodeResource(getResources(),
R.drawable.background_plane));
mapView.getOptions().setClearColor(Color.WHITE);
// configure texture caching - optional, suggested
mapView.getOptions().setTextureMemoryCacheSize(40 * 1024 * 1024);
mapView.getOptions().setCompressedMemoryCacheSize(8 * 1024 * 1024);
// define online map persistent caching - optional, suggested. Default - no caching
mapView.getOptions().setPersistentCachePath(this.getDatabasePath("mapcache").getPath());
// set persistent raster cache limit to 100MB
mapView.getOptions().setPersistentCacheSize(100 * 1024 * 1024);
// 4. Start the map - mandatory
mapView.startMapping();
// 5. Add simple marker to map.
// define marker style (image, size, color)
Bitmap pointMarker = UnscaledBitmapLoader.decodeResource(getResources(), R.drawable.olmarker);
MarkerStyle markerStyle = MarkerStyle.builder().setBitmap(pointMarker).setSize(0.5f).setColor(Color.WHITE).build();
// define label what is shown when you click on marker
Label markerLabel = new DefaultLabel("San Francisco", "Here is a marker");
// define location of the marker, it must be converted to base map coordinate system
MapPos markerLocation = mapLayer.getProjection().fromWgs84(-122.416667f, 37.766667f);
// create layer and add object to the layer, finally add layer to the map.
// All overlay layers must be same projection as base layer, so we reuse it
MarkerLayer markerLayer = new MarkerLayer(mapLayer.getProjection());
markerLayer.add(new Marker(markerLocation, markerLabel, markerStyle, null));
mapView.getLayers().addLayer(markerLayer);
// add event listener
MyLocationMapEventListener mapListener = new MyLocationMapEventListener(this, mapView);
mapView.getOptions().setMapListener(mapListener);
// add GPS My Location functionality
MyLocationCircle locationCircle = new MyLocationCircle();
mapListener.setLocationCircle(locationCircle);
initGps(locationCircle);
}
#Override
public Object onRetainNonConfigurationInstance() {
Log.debug("onRetainNonConfigurationInstance");
return this.mapView.getComponents();
}
protected void initGps(final MyLocationCircle locationCircle) {
final Projection proj = mapView.getLayers().getBaseLayer().getProjection();
LocationListener locationListener = new LocationListener()
{
public void onLocationChanged(Location location) {
if (locationCircle != null) {
locationCircle.setLocation(proj, location);
locationCircle.setVisible(true);
mapView.setFocusPoint(mapView.getLayers().getBaseLayer().getProjection().fromWgs84(location.getLongitude(), location.getLatitude()));
}
}
public void onStatusChanged(String provider, int status, Bundle extras) {
Log.debug("GPS onStatusChanged "+provider+" to "+status);
}
public void onProviderEnabled(String provider) {
Log.debug("GPS onProviderEnabled");
}
public void onProviderDisabled(String provider) {
Log.debug("GPS onProviderDisabled");
}
};
LocationManager locationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);
locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 10000, 100, locationListener);
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 1000, 0, locationListener);
}
ps.This is excactly i wand to do but in android sdk http://openplans.github.io/Leaflet.AnimatedMarker/
You can just use setMapPos() method of Markers in Nutiteq SDK.

Android- Draw Path from KML File in MapView

I've been working on this for a while now, but I still have not gotten the result I'm looking for. I'm trying to create an app to display our mountain bike trails so you can see where you are when you're on them. I started by manually copying coordinates from my kml file and pasting them as Geopoints with this code which worked until I got to point 80. Then the LogCat gave an error rejecting large method, and I still have hundreds more to go:
import java.util.List;
import com.google.android.maps.GeoPoint;
import com.google.android.maps.MapActivity;
import com.google.android.maps.MapController;
import com.google.android.maps.MapView;
import com.google.android.maps.Overlay;
import com.google.android.maps.Projection;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Point;
import android.os.Bundle;
public class TrailMaps extends MapActivity {
private List mapOverlays;
private Projection projection;
private MapController mc;
private MapView mapView;
private GeoPoint gP;
private MyOverlay myoverlay;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mapView = (MapView) findViewById(R.id.mapview);//Creating an instance of MapView
mapView.setBuiltInZoomControls(true);//Enabling the built-in Zoom Controls
gP = new GeoPoint(43311836,-91777756);//Creating a GeoPoint
mc = mapView.getController();
mc.setCenter(gP);
mc.setZoom(15);//Initializing the MapController and setting the map to center at the
mapOverlays = mapView.getOverlays();
projection = mapView.getProjection();
myoverlay = new MyOverlay();
mapOverlays.add(myoverlay);
}
class MyOverlay extends Overlay{
public MyOverlay(){
}
public void draw(Canvas canvas, MapView mapView, boolean shadow) {
super.draw(canvas, mapView, shadow);
Paint paint;
paint = new Paint();
paint.setDither(true);
paint.setColor(Color.RED);
paint.setStyle(Paint.Style.FILL_AND_STROKE);
paint.setStrokeJoin(Paint.Join.ROUND);
paint.setStrokeWidth(2);
GeoPoint gp1 = new GeoPoint(43311836,-91777756);
GeoPoint gp2 = new GeoPoint(43311718,-91777699);
GeoPoint gp3 = new GeoPoint(43311718,-91777699);
GeoPoint gp4 = new GeoPoint(43311666,-91777627);
GeoPoint gp5 = new GeoPoint(43311624,-91777541);
Point pt1 = new Point();
Point pt2 = new Point();
Point pt3 = new Point();
Point pt4 = new Point();
Point pt5 = new Point();
Path path1 = new Path();
Path path2 = new Path();
Path path3 = new Path();
projection.toPixels(gp1,pt1);
projection.toPixels(gp2, pt2);
projection.toPixels(gp3, pt3);
projection.toPixels(gp4, pt4);
projection.toPixels(gp5, pt5);
path1.moveTo(pt1.x, pt1.y);
path1.lineTo(pt2.x, pt2.y);
path2.moveTo(pt3.x,pt3.y);
path2.lineTo(pt4.x, pt4.y);
path3.moveTo(pt4.x,pt4.y);
path3.lineTo(pt5.x,pt5.y);
canvas.drawPath(path1, paint);
canvas.drawPath(path2, paint);
canvas.drawPath(path3, paint);
}
}
#Override
protected boolean isRouteDisplayed() {
return false;
}
}
Then I tried to parse the kml file as in this example: How to draw a path on a map using kml file?
But this is taking my coordinates and finding the nearest streets and overlaying a route. My coordinates are not along the street they are in a park off road, so how else should I do it?
Right now I'm open to any suggestions. Is there a better way to parse the kml file, do it locally vs going to http://maps.google.com/maps?, overlaying it as an image, or a whole new direction completely... Any suggestions would be much appreciated.
Thanks

Android map performance with > 800 overlays of KML data

I have some a shape file which I have converted to a KML file that I wish to read coordinates from and then draw paths between the coordinates on a MapView. With the help of this great post: How to draw a path on a map using kml file? I have been able to read the the KML into an ArrayList of "Placemarks". This great blog post then showed how to take a list of GeoPoints and draw a path: http://djsolid.net/blog/android---draw-a-path-array-of-points-in-mapview
The example in the above post only draws one path between some points however and since I have many more paths than that I am running into some performance problems. I'm currently adding a new RouteOverlay for each of the separate paths. This results in me having over 800 overlays when they have all been added. This has a performance hit and I would love some input on what I can do to improve it.
Here are some options I have considered:
Try to add all the points to a List which then can be passed into a class that will extend Overlay. In that new class perhaps it would be possible to add and draw the paths in a single Overlay layer? I'm not sure on how to implement this though since the paths are not always intersecting and they have different start and end points. At the moment I'm adding each path which has several points to it's own list and then I add that to an Overlay. That results in over 700 overlays...
Simplify the KML or SHP. Instead of having over 700 different paths, perhaps there is someway to merge them into perhaps 100 paths or less? Since alot of paths are intersected at some point it should be possible to modify the original SHP file so that it merges all intersections. Since I have never worked with these kinds of files before I have not been able to find a way to do this in GQIS. If someone knows how to do this I would love for some input on that. Here is a link to the group of shape files if you are interested:
http://danielkvist.net/cprg_bef_cbana_polyline.shp
http://danielkvist.net/cprg_bef_cbana_polyline.shx
http://danielkvist.net/cprg_bef_cbana_polyline.dbf
http://danielkvist.net/cprg_bef_cbana_polyline.prj
Anyway, here is the code I'm using to add the Overlays. Many thanks in advance.
RoutePathOverlay.java
package net.danielkvist;
import java.util.List;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Point;
import android.graphics.RectF;
import com.google.android.maps.GeoPoint;
import com.google.android.maps.MapView;
import com.google.android.maps.Overlay;
import com.google.android.maps.Projection;
public class RoutePathOverlay extends Overlay
{
private int _pathColor;
private final List<GeoPoint> _points;
private boolean _drawStartEnd;
public RoutePathOverlay(List<GeoPoint> points)
{
this(points, Color.RED, false);
}
public RoutePathOverlay(List<GeoPoint> points, int pathColor, boolean drawStartEnd)
{
_points = points;
_pathColor = pathColor;
_drawStartEnd = drawStartEnd;
}
private void drawOval(Canvas canvas, Paint paint, Point point)
{
Paint ovalPaint = new Paint(paint);
ovalPaint.setStyle(Paint.Style.FILL_AND_STROKE);
ovalPaint.setStrokeWidth(2);
int _radius = 6;
RectF oval = new RectF(point.x - _radius, point.y - _radius, point.x + _radius, point.y + _radius);
canvas.drawOval(oval, ovalPaint);
}
public boolean draw(Canvas canvas, MapView mapView, boolean shadow, long when)
{
Projection projection = mapView.getProjection();
if (shadow == false && _points != null)
{
Point startPoint = null, endPoint = null;
Path path = new Path();
// We are creating the path
for (int i = 0; i < _points.size(); i++)
{
GeoPoint gPointA = _points.get(i);
Point pointA = new Point();
projection.toPixels(gPointA, pointA);
if (i == 0)
{ // This is the start point
startPoint = pointA;
path.moveTo(pointA.x, pointA.y);
}
else
{
if (i == _points.size() - 1)// This is the end point
endPoint = pointA;
path.lineTo(pointA.x, pointA.y);
}
}
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setColor(_pathColor);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(3);
paint.setAlpha(90);
if (getDrawStartEnd())
{
if (startPoint != null)
{
drawOval(canvas, paint, startPoint);
}
if (endPoint != null)
{
drawOval(canvas, paint, endPoint);
}
}
if (!path.isEmpty())
canvas.drawPath(path, paint);
}
return super.draw(canvas, mapView, shadow, when);
}
public boolean getDrawStartEnd()
{
return _drawStartEnd;
}
public void setDrawStartEnd(boolean markStartEnd)
{
_drawStartEnd = markStartEnd;
}
}
MyMapActivity
package net.danielkvist;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import android.graphics.Color;
import android.os.Bundle;
import android.util.Log;
import com.google.android.maps.GeoPoint;
import com.google.android.maps.MapActivity;
import com.google.android.maps.MapView;
public class MyMapActivity extends MapActivity
{
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
MapView mapView = (MapView) findViewById(R.id.mapview);
mapView.setBuiltInZoomControls(true);
String url = "http://danielkvist.net/cprg_bef_cbana_polyline_simp1600.kml";
NavigationDataSet set = MapService.getNavigationDataSet(url);
drawPath(set, Color.parseColor("#6C8715"), mapView);
}
/**
* Does the actual drawing of the route, based on the geo points provided in
* the nav set
*
* #param navSet
* Navigation set bean that holds the route information, incl.
* geo pos
* #param color
* Color in which to draw the lines
* #param mMapView01
* Map view to draw onto
*/
public void drawPath(NavigationDataSet navSet, int color, MapView mMapView01)
{
ArrayList<GeoPoint> geoPoints = new ArrayList<GeoPoint>();
Collection overlaysToAddAgain = new ArrayList();
for (Iterator iter = mMapView01.getOverlays().iterator(); iter.hasNext();)
{
Object o = iter.next();
Log.d(BikeApp.APP, "overlay type: " + o.getClass().getName());
if (!RouteOverlay.class.getName().equals(o.getClass().getName()))
{
overlaysToAddAgain.add(o);
}
}
mMapView01.getOverlays().clear();
mMapView01.getOverlays().addAll(overlaysToAddAgain);
int totalNumberOfOverlaysAdded = 0;
for(Placemark placemark : navSet.getPlacemarks())
{
String path = placemark.getCoordinates();
if (path != null && path.trim().length() > 0)
{
String[] pairs = path.trim().split(" ");
String[] lngLat = pairs[0].split(","); // lngLat[0]=longitude
// lngLat[1]=latitude
// lngLat[2]=height
try
{
if(lngLat.length > 1 && !lngLat[0].equals("") && !lngLat[1].equals(""))
{
GeoPoint startGP = new GeoPoint(
(int) (Double.parseDouble(lngLat[1]) * 1E6),
(int) (Double.parseDouble(lngLat[0]) * 1E6));
GeoPoint gp1;
GeoPoint gp2 = startGP;
geoPoints = new ArrayList<GeoPoint>();
geoPoints.add(startGP);
for (int i = 1; i < pairs.length; i++)
{
lngLat = pairs[i].split(",");
gp1 = gp2;
if (lngLat.length >= 2 && gp1.getLatitudeE6() > 0
&& gp1.getLongitudeE6() > 0
&& gp2.getLatitudeE6() > 0
&& gp2.getLongitudeE6() > 0)
{
// for GeoPoint, first:latitude, second:longitude
gp2 = new GeoPoint(
(int) (Double.parseDouble(lngLat[1]) * 1E6),
(int) (Double.parseDouble(lngLat[0]) * 1E6));
if (gp2.getLatitudeE6() != 22200000)
{
geoPoints.add(gp2);
}
}
}
totalNumberOfOverlaysAdded++;
mMapView01.getOverlays().add(new RoutePathOverlay(geoPoints));
}
}
catch (NumberFormatException e)
{
Log.e(BikeApp.APP, "Cannot draw route.", e);
}
}
}
Log.d(BikeApp.APP, "Total overlays: " + totalNumberOfOverlaysAdded);
mMapView01.setEnabled(true);
}
#Override
protected boolean isRouteDisplayed()
{
// TODO Auto-generated method stub
return false;
}
}
Edit: There are of course some more files I'm using but that I have not posted. You can download the complete Eclipse project here: http://danielkvist.net/se.zip
Have you considered rendering all the paths to a bitmap and then use that as a overlay, and of-course you would need to render it again if the user zooms in or out or moves the map alot. Making the bitmap 2 or 4 times as large as the screen (be care-full not to use up all memory here) you should be able to get some zooming in and out a-swell as allow for a bit of panning until you need to render it again.
Rendering it like a quad-tree (with week references to the bitmaps in the tree) would allow for some caching and possibly big performance improvements.
Going with a quad-tree is not an easy approach but might be worth the effort if you have the time and knowledge for it. I believe this roughly is how the google maps handles its map tiles.

Categories

Resources