i'm trying to get the coordinates of my kml layer in my android app, and i just cant seem to find how to do it.
i have this kml layer:
KmlLayer layer = new KmlLayer(mMap, R.raw.allowedarea, getApplicationContext());
and i'm trying to get the Latitude and Longtitude list of his boundries points.
ArrayList<LatLnt> latlitArray = layer.soemthing();
could find anything, please guys help.
Try this solution
try {
KmlLayer layer = new KmlLayer(googleMap, R.raw.zone, this);
layer.addLayerToMap();
Iterable<KmlContainer> containers = layer.getContainers();
accessContainers(containers);
} catch (XmlPullParserException | IOException e) {
e.printStackTrace();
}
public void accessContainers(Iterable<KmlContainer> containers) {
for (KmlContainer container : containers) {
if (container != null) {
if (container.hasContainers()) {
accessContainers(container.getContainers());
} else {
if (container.hasPlacemarks()) {
accessPlacemarks(container.getPlacemarks());
}
}
}
}
}
public void accessPlacemarks(Iterable<KmlPlacemark> placemarks) {
for (KmlPlacemark placemark : placemarks) {
if (placemark != null) {
KmlGeometry geometry = placemark.getGeometry();
if (geometry instanceof KmlPolygon) {
KmlPolygon polygon = (KmlPolygon) geometry;
mLatLngList.addAll(polygon.getOuterBoundaryCoordinates());
}
}
}
}
This will recursively access every placemark geometry inside the container. I'm not aware if the object obtained can actually be an instance of any other class or collection besides List and LatLng.
public void accessContainers(Iterable<KmlContainer> containers) {
for(KmlContainer c : containers) {
if(c.hasPlacemarks()) {
for(KmlPlacemark p : c.getPlacemarks()) {
KmlGeometry g = p.getGeometry();
Object object = g.getGeometryObject();
if(object instanceof LatLng) {
LatLng latlng = (LatLng)object;
//Do more stuff with the point
}
if(object instanceof List<?>) {
List<LatLng> list = (List<LatLng>)object;
//Do more stuff with the list of points
}
Log.d(TAG, g.getGeometryType() + ":" + object.toString());
}
}
if(c.hasContainers()) {
accessContainers(c.getContainers());
}
}
}
Related
I want to know a simple and easy way to parse KML file and store its data in an object so that I can access its data instantly here is my kml file
You can use KmlContainer from Google Maps KML Importing Utility to access any property in a container:
...
KmlLayer layer = new KmlLayer(getMap(), kmlInputStream, getApplicationContext());
Iterable containers = layer.getContainers();
for (KmlContainer container : containers ) {
if (container.hasProperty("property_name")) {
// process property
Log.d(TAG, "" + container.getProperty("property_name"));
}
}
...
For exactly yours kml file for standard geometry you can use something like this:
#Override
public void onMapReady(GoogleMap googleMap) {
mGoogleMap = googleMap;
mGoogleMap.animateCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(17.425868, 78.459761), 16));
// change next line for your kml source
InputStream kmlInputStream = getResources().openRawResource(R.raw.data);
try {
KmlLayer kmlLayer = new KmlLayer(mGoogleMap, kmlInputStream, getApplicationContext());
kmlLayer.addLayerToMap();
ArrayList<LatLng> pathPoints = new ArrayList();
if (kmlLayer != null && kmlLayer.getContainers() != null) {
for (KmlContainer container : kmlLayer.getContainers()) {
if (container.hasPlacemarks()) {
for (KmlPlacemark placemark : container.getPlacemarks()) {
Geometry geometry = placemark.getGeometry();
if (geometry.getGeometryType().equals("Point")) {
KmlPoint point = (KmlPoint) placemark.getGeometry();
LatLng latLng = new LatLng(point.getGeometryObject().latitude, point.getGeometryObject().longitude);
pathPoints.add(latLng);
} else if (geometry.getGeometryType().equals("LineString")) {
KmlLineString kmlLineString = (KmlLineString) geometry;
ArrayList<LatLng> coords = kmlLineString.getGeometryObject();
for (LatLng latLng : coords) {
pathPoints.add(latLng);
}
}
}
}
}
for (LatLng latLng : pathPoints) {
mGoogleMap.addMarker(new MarkerOptions().position(latLng));
}
}
} catch (XmlPullParserException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
and get something like that:
but for <ExtendedData> you should use external library with KML parsing support like GeoTools or parse your KML file as XML.
I'm working with mapbox android, i'm trying to add multiple waypoints between origin and destination.but after adding one waypoint when it's adding another one this gives the exception " Too many coordinate the s; maximum number of coordinates is 3."
I just want to add multiple waypoint between two point and draw route over those line in mapbox android.
[pastbin link] : https://paste.ubuntu.com/p/PKMQzFyzVb/
My Route Draw Function -->
{
private void getRouteWithWaypoint(Point origin, Point destination, List<Point> wayPoints) {
assert Mapbox.getAccessToken() != null;
NavigationRoute.Builder builder = NavigationRoute.builder(getActivity())
.accessToken(Mapbox.getAccessToken())
.origin(origin)
.destination(destination);
if (wayPoints != null) {
for (Point point : wayPoints) {
builder.addWaypoint(point);
}
}
builder.build().getRoute(new Callback<DirectionsResponse>() {
#Override
public void onResponse(#NonNull Call<DirectionsResponse> call, Response<DirectionsResponse> response) {
Log.e(TAG, "Response code: " + response.code());
if (response.body() == null) {
Log.e(TAG, "No routes found, make sure you set the right user and access token.");
return;
} else if (response.body().routes().size() < 1) {
Log.e(TAG, "No routes found");
return;
}
currentRoute = response.body().routes().get(0);
if (navigationMapRoute != null) {
navigationMapRoute.removeRoute();
} else {
navigationMapRoute = new NavigationMapRoute(null, mapView, map, R.style.NavigationMapRoute);
}
navigationMapRoute.addRoute(currentRoute);
}
#SuppressLint("TimberArgCount")
#Override
public void onFailure(Call<DirectionsResponse> call, Throwable t) {
Timber.e(t, "Error: %s");
}
});
}}
Draw root on Mapbox map, copied the below code from Mapbox documentation.
private void getRoute(Point origin, Point destination) {
NavigationRoute.builder(this)
.accessToken(Mapbox.getAccessToken())
.origin(origin)
.destination(destination)
.build()
.getRoute(new Callback<DirectionsResponse>() {
#Override
public void onResponse(Call<DirectionsResponse> call, Response<DirectionsResponse> response) {
// You can get the generic HTTP info about the response
Log.d(TAG, "Response code: " + response.code());
if (response.body() == null) {
Log.e(TAG, "No routes found, make sure you set the right user and access token.");
return;
} else if (response.body().routes().size() < 1) {
Log.e(TAG, "No routes found");
return;
}
currentRoute = response.body().routes().get(0);
// Draw the route on the map
if (navigationMapRoute != null) {
navigationMapRoute.removeRoute();
} else {
navigationMapRoute = new NavigationMapRoute(null, mapView, mapboxMap, R.style.NavigationMapRoute);
}
navigationMapRoute.addRoute(currentRoute);
}
#Override
public void onFailure(Call<DirectionsResponse> call, Throwable throwable) {
Log.e(TAG, "Error: " + throwable.getMessage());
}
});
}
for more detail follow the link
https://www.mapbox.com/help/android-navigation-sdk/#calculate-and-draw-route
The default profile for requesting routes is DirectionsCriteria.ProfileCriteria.PROFILE_DRIVING_TRAFFIC
This profile allows only 1 waypoint between the origin and the destination. If you want to use more than 1 waypoint, just use PROFILE_DRIVING instead (this allows up to 25 waypoints I think).
Like this:
NavigationRoute.Builder builder = NavigationRoute.builder(getActivity())
.accessToken(Mapbox.getAccessToken())
.origin(origin)
.destination(destination)
.profile(DirectionsCriteria.ProfileCriteria.PROFILE_DRIVING);
if (wayPoints != null) {
for (Point point : wayPoints) {
builder.addWaypoint(point);
}
}
After updating version of forms to 2.4.0.282, I started getting this weird behavior in MapView. I have created a custom renderer for map in android where I am setting marker images as per my requirement. Custom markers in fact appear, but on top of it, it's default icon still gets overlaid.
Note that I am using Xamarin.Maps version 2.4.0.282, tried to downgrade to prior versions as well but I am of no help.
I even tried it out by commenting line,
Forms.SetFlags("FastRenderers_Experimental");
but even this didn't help.
Below is the renderer I created,
public class CustomMapRenderer : MapRenderer, IOnMapReadyCallback
{
GoogleMap map;
public static double PreviousDistance = 0;
List<CustomPin> customPins;
CustomMap formsMap = null;
protected override void OnElementChanged(Xamarin.Forms.Platform.Android.ElementChangedEventArgs<Map> e)
{
base.OnElementChanged(e);
if (e.OldElement != null)
{
map.InfoWindowClick -= OnInfoWindowClick;
map.MarkerClick -= OnMarkerClick;
}
if (e.NewElement != null)
{
formsMap = (CustomMap)e.NewElement;
customPins = formsMap.CustomPins;
((MapView)Control).GetMapAsync(this);
}
}
protected override void OnMapReady(GoogleMap googleMap)
{
map = googleMap;
map.InfoWindowClick += OnInfoWindowClick;
map.MarkerClick += OnMarkerClick;
map.UiSettings.ZoomControlsEnabled = false;
formsMap.MoveToRegion(MapSpan.FromCenterAndRadius(formsMap.Location, Distance.FromMiles(1.0)));
if(customPins != null && customPins.Count > 0)
{
setMapPins("CustomPins");
}
}
protected override void OnElementPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (e.PropertyName == "CustomPins" || (e.PropertyName.Equals("VisibleRegion")))
{
setMapPins(e.PropertyName);
}
}
private void setMapPins(string PropertyName)
{
customPins = formsMap.CustomPins;
map.Clear();
if (customPins != null && customPins.Count > 0)
{
if (PropertyName == "CustomPins")
{
//Set map zoom
var defaultZoom = 14;
try
{
PreviousDistance = DistanceCalculation.MoveToRegionData.MoveToRegion(formsMap, customPins, defaultZoom, PreviousDistance);
}
catch (Exception ex)
{
PreviousDistance = 0;
Console.WriteLine(ex.Message);
}
}
foreach (var pin in customPins)
{
var pinImage = Resources.GetIdentifier(pin.PinImage.ToLower(), "drawable", Context.PackageName);
var markerImg = BitmapDescriptorFactory.FromResource(pinImage);
map.AddMarker(new MarkerOptions().SetTitle(pin.Pin.Label).SetSnippet(pin.Id).SetPosition(new LatLng(pin.Pin.Position.Latitude, pin.Pin.Position.Longitude)).SetIcon(markerImg));
}
}
else
{
Console.WriteLine("In else!!");
}
}
protected override void OnLayout(bool changed, int l, int t, int r, int b)
{
base.OnLayout(changed, l, t, r, b);
if (changed)
{
}
}
void OnInfoWindowClick(object sender, GoogleMap.InfoWindowClickEventArgs e)
{
var customPin = GetCustomPin(e.Marker);
if (customPin == null)
{
throw new Exception("Custom pin not found");
}
if (!string.IsNullOrWhiteSpace(customPin.Url))
{
var url = global::Android.Net.Uri.Parse(customPin.Url);
var intent = new Intent(Intent.ActionView, url);
intent.AddFlags(ActivityFlags.NewTask);
global::Android.App.Application.Context.StartActivity(intent);
}
}
CustomPin GetCustomPin(Marker annotation)
{
var position = new Position(annotation.Position.Latitude, annotation.Position.Longitude);
foreach (var pin in customPins)
{
if (pin.Pin.Position == position)
{
return pin;
}
}
return null;
}
void OnMarkerClick(object sender, GoogleMap.MarkerClickEventArgs ea)
{
var marker = (Marker) ea.Marker;
formsMap.IsPinClicked = false;
var customPin = GetCustomPin(marker);
if (customPin == null)
{
throw new Exception("Custom pin not found");
}
formsMap.SelectedPinId = Convert.ToInt32(marker.Snippet);
formsMap.IsPinClicked = true;
}
}
This is what it looks like right now..
I have created a custom renderer for map in android where I am setting marker images as per my requirement. Custom markers in fact appear, but on top of it, it's default icon still gets overlaid.
I've made a demo and reproduced the problem, after some tests I found that the overriding of OnMapReady function causes the problem. Even with totally empty OnMapReady the problem will occurs. My guess is a call to the OnMapReady in Custom Map Render might lead to a rerender of the Map including pins.
Solution:
Comment OnMapReady out.
Move the logic in OnMapReady to OnElementChanged.
Let your local variable map=NativeMap and make sure setMapPins get called in OnElementPropertyChanged.
Add NativeMap.Clear(); in OnElementPropertyChanged and try again. Hope it will help!!!
Have a look below code for customizing pin on Android Platform.
[assembly:ExportRenderer (typeof(CustomMap), typeof(CustomMapRenderer))]
namespace CustomRenderer.Droid
{
public class CustomMapRenderer : MapRenderer, GoogleMap.IInfoWindowAdapter
{
List<CustomPin> customPins;
bool isDrawn;
protected override void OnElementChanged(Xamarin.Forms.Platform.Android.ElementChangedEventArgs<View> e)
{
base.OnElementChanged(e);
if (e.OldElement != null)
{
NativeMap.InfoWindowClick -= OnInfoWindowClick;
}
if (e.NewElement != null)
{
var formsMap = (CustomMap)e.NewElement;
customPins = formsMap.CustomPins;
((MapView)Control).GetMapAsync(this);
}
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (e.PropertyName.Equals("VisibleRegion") && !isDrawn)
{
NativeMap.Clear();
NativeMap.InfoWindowClick += OnInfoWindowClick;
NativeMap.SetInfoWindowAdapter(this);
foreach (var pin in customPins)
{
var marker = new MarkerOptions();
marker.SetPosition(new LatLng(pin.Pin.Position.Latitude, pin.Pin.Position.Longitude));
marker.SetTitle(pin.Pin.Label);
marker.SetSnippet(pin.Pin.Address);
marker.SetIcon(BitmapDescriptorFactory.FromResource(Resource.Drawable.pin));
NativeMap.AddMarker(marker);
}
isDrawn = true;
}
}
}
}
For more detail Click here
I know exactly why I'm getting this error but I can't figure out how to fix it. So What I'm doing it drawing a bunch of markers using Picasso where I implement a Target and load the Marker Icon once the image is done download. However, the user is able to click a tab which would clear the map view. So in an effort to try and fix it I don't use getMap.clear() but rather track the markers in a hashmap and when the map should clear I loop through the hashmap and remove the marker from the map and clear the hashmap, however I'm still getting this issue.
Here is the function to remove the markers:
private void clearCars() throws IllegalArgumentException {
Iterator it = vehicles.entrySet().iterator();
while (it.hasNext()) {
Map.Entry pair = (Map.Entry) it.next();
Car aCar = (Car) pair.getValue();
if (aCar.getCurrentMarker() != null) {
if (aCar.getCurrentMarker().getmMarker() != null) {
aCar.getCurrentMarker().getmMarker().remove();
}
}
}
vehicles.clear();
}
This method draws the cars:
private void validateMap(ArrayList<Object> data) {
if (Theme.isCarTheme()) {
for (Object anObject : data) {
Car currentCar = (Car) anObject;
String uniqueIndex = currentCar.getCarID() + "-" + currentCar.getVehicleNumber();
CustomMarker aMarker = null;
if (vehicles.containsKey(uniqueIndex)) {
aMarker = ((Car) vehicles.get(uniqueIndex)).getCurrentMarker();
aMarker.getmMarker().setPosition(currentCar.getCarLocation());
} else {
MarkerOptions options = new MarkerOptions();
options.icon(BitmapDescriptorFactory.fromResource(R.drawable.blank_image));
options.position(currentCar.getCarLocation());
currentCar.setCurrentMarker(new CustomMarker(mapViewLayout.getMapView().getMap().addMarker(options)));
String strUrl = Constants.Car_IMAGE_URL + currentCar.getCarID() + assetType + ".png";
Picasso.with(getActivity()).load(strUrl).into(currentCar.getCurrentMarker());
vehicles.put(uniqueIndex, currentCar);
}
vehicles.get(uniqueIndex).setHasUpdated(true);
}
Iterator it2 = vehicles.entrySet().iterator();
while (it2.hasNext()) {
Map.Entry pair = (Map.Entry) it2.next();
Car aCar = (Car) pair.getValue();
if (aCar.isHasUpdated() == false) {
it2.remove();
aCar.getCurrentMarker().getmMarker().remove();
}
}
}
}
And this is the marker class:
public class CustomMarker implements Target {
Marker mMarker;
public CustomMarker(Marker marker) {
mMarker = marker;
}
#Override
public int hashCode() {
return mMarker.hashCode();
}
#Override
public boolean equals(Object o) {
if(o instanceof CustomMarker) {
Marker marker = ((CustomMarker) o).mMarker;
return mMarker.equals(marker);
} else {
return false;
}
}
#Override
public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) throws IllegalArgumentException {
mMarker.setIcon(BitmapDescriptorFactory.fromBitmap(bitmap));
}
#Override
public void onBitmapFailed(Drawable errorDrawable) {
}
#Override
public void onPrepareLoad(Drawable placeHolderDrawable) {
}
public Marker getmMarker() {
return mMarker;
}
}
So after finally getting my head around Xamarin.Forms DependencyService I have nearly got it returning the device's current location.
my interface
public interface ICurrentLocation
{
MyLocation SetCurrentLocation();
}
MyLocation
public class MyLocation
{
public double Latitude {get; set;}
public double Longitude{get; set;}
}
the line that calls it
MyLocation location = DependencyService.Get<ICurrentLocation>().SetCurrentLocation();
and in the CurrentLocation class in the Android project that implements the Geolocation class of Xamarin.Mobile
[assembly: Dependency(typeof(CurrentLocation))]
namespace MyCockburn.Droid
{
public class CurrentLocation : Activity, ICurrentLocation
Geolocator locator;
Position position = new Position();
MyLocation location;
public MyLocation SetCurrentLocation()
{
GetPosition();
location = new MyLocation()
{
Latitude = position.Latitude,
Longitude = position.Longitude
};
return location;
}
async void GetPosition()
{
try
{
locator = new Geolocator(this) { DesiredAccuracy = 50 };
if (locator.IsListening != true)
locator.StartListening(minTime: 1000, minDistance: 0);
position = await locator.GetPositionAsync(timeout: 20000);
}
catch (Exception e)
{
Log.Debug("GeolocatorError", e.ToString());
}
}
}
my problem seems to be that is returning location before position holds the longitude and latitude
I am hoping my mistake is glaringly obvious
EDIT: the code works if I run it as a normal Android Activity
I would do a slight modification since best practice is to either do all async or none. When you try to return the result from an async method from a non async method you can run into problems with deadlocks. Also, since you aren't using the await keyword when calling the GetPosition method, you are getting back a Task, but aren't checking when the operation is complete. I suggest slightly modifying your code as such.
public interface ICurrentLocation {
Task<MyLocation> GetCurrentLocation();
}
public async Task<MyLocation> GetCurrentLocation()
{
var position = await GetPosition();
return new MyLocation()
{
Latitude = position.Latitude,
Longitude = position.Longitude
};
}
async Task<Location> GetPosition()
{
try
{
locator = new Geolocator(this) { DesiredAccuracy = 50 };
if (locator.IsListening != true)
locator.StartListening(minTime: 1000, minDistance: 0);
return await locator.GetPositionAsync(timeout: 20000);
}
catch (Exception e)
{
Log.Debug("GeolocatorError", e.ToString());
}
}
You aren't waiting for the position function to finish. Many different options and keeping it async is the best one but if you want it synchronous then try this blocking call:
void GetPosition()
{
try
{
locator = new Geolocator(this) { DesiredAccuracy = 50 };
if (locator.IsListening != true)
locator.StartListening(minTime: 1000, minDistance: 0);
position = locator.GetPositionAsync(timeout: 20000).Result;
}
catch (Exception e)
{
Log.Debug("GeolocatorError", e.ToString());
}
}
I also recommend taking a look at Xamarin.Forms.Labs as it already has abstracted GPS service and working sample that is functional for all 3 platforms:
https://github.com/XForms/Xamarin-Forms-Labs
Try adding the assembly above the namespace and awaiting the GetPosition method.
Take a look at this image:
http://developer.xamarin.com/guides/cross-platform/xamarin-forms/dependency-service/Images/solution.png
I developed an app that works fine in getting GPS location. I believe the codes below will be of great help.
You can then edit the SubmitGPSLocation function to your preference.
public async Task Run(CancellationToken token)
{
await Task.Run(async () =>
{
if (GPSService.Instance.IsListening)
{
GPSService.Instance.StopListening();
}
GPSService.Instance.StartListening(2500, 50, true);
GPSService.Instance.PositionChanged += Instance_PositionChanged;
System.Diagnostics.Debug.WriteLine(getRunningStateLocationService());
while (getRunningStateLocationService())
{
token.ThrowIfCancellationRequested();
await Task.Delay(500).ConfigureAwait(true);
}
GPSService.Instance.StopListening();
//await CrossGeolocator.Current.StopListeningAsync().ConfigureAwait(true);
GPSService.Instance.PositionChanged -= Instance_PositionChanged;
return;
}, token).ConfigureAwait(false);
}
private void Instance_PositionChanged(object sender, PositionEventArgs e)
{
try
{
isEvenCount = !isEvenCount;
if (e.Position != null)
{
var message = new LocationMessage
{
Latitude = e.Position.Latitude,
Longitude = e.Position.Longitude,
Accuracy = e.Position.Accuracy,
Speed = e.Position.Speed,
Heading = e.Position.Heading,
TimeStamp = e.Position.Timestamp.DateTime
};
SubmitGPSLocation(e).ConfigureAwait(false);
}
else
{
CrossToastPopUp.Current.ShowToastMessage("Failed to get GPS location");
}
}
catch (Exception ex)
{
CrossToastPopUp.Current.ShowToastMessage(ex.Message);
}
}
private static async Task SubmitGPSLocation(PositionEventArgs e)
{
if (!NetworkCheck.IsInternet())
{
return;
}
if (!int.TryParse(App.PhoneID, out var number))
{
return;
}
try
{
var thetrackers = Convert.ToString(Application.Current.Properties["AuthorizedTrackers"]);
GeneralUserPhoneLocation MyGeneralUserPhoneLocation = new GeneralUserPhoneLocation();
MyGeneralUserPhoneLocation.PhoneID = int.Parse(App.PhoneID);
MyGeneralUserPhoneLocation.Latitude = e.Position.Latitude.ToString("n6");
MyGeneralUserPhoneLocation.Longitude = e.Position.Longitude.ToString("n6");
MyGeneralUserPhoneLocation.Accuracy = e.Position.Accuracy;
MyGeneralUserPhoneLocation.Heading = e.Position.Heading;
MyGeneralUserPhoneLocation.Speed = e.Position.Speed;
MyGeneralUserPhoneLocation.TimeStamp = e.Position.Timestamp.DateTime;
MyGeneralUserPhoneLocation.RequestType = "N";
MyGeneralUserPhoneLocation.Comment = thetrackers;
string servicestring = JsonConvert.SerializeObject(MyGeneralUserPhoneLocation);
HttpContent theusercontent = new StringContent(servicestring, Encoding.UTF8, "application/json");
using (HttpClient client = new HttpClient())
{
client.BaseAddress = new Uri("https://mygpswebapi.com");
var response = await client.PostAsync("Home/SaveGeneralUserPhoneLocationAPP/", theusercontent).ConfigureAwait(true);
if (response.IsSuccessStatusCode)
{
}
else
{
}
}
}
catch (Exception ex)
{
CrossToastPopUp.Current.ShowToastMessage(ex.Message);
}
}