I'm migrating from Google Maps API to Apple MapKit JS for the simple reason I have a developer account with them and they offer more free hits.
Anyway, actual examples of MapKit JS are a bit thin (or at least Google isn't finding them - draw what conspiracy theories you will), so although I've got the basics going of displaying an embeded map, I can't seem to do the next step which is route between two points (Apple's documentation also seems impenetrable as they don't show examples).
Here's my script for a basic map:
<script>
mapkit.init({
authorizationCallback: function(done) {
done('[MY-TOKEN]');
}
});
var MarkerAnnotation = mapkit.MarkerAnnotation
var myMarker = new mapkit.Coordinate(55.9496320, -3.1866360)
var myRegion = new mapkit.CoordinateRegion(
new mapkit.Coordinate(55.9496320, -3.1866360),
new mapkit.CoordinateSpan(0.003, 0.003)
);
var map = new mapkit.Map("map");
var myAnnotation = new MarkerAnnotation(myMarker, { color: "#9b6bcc", title: "theSpace On The Mile"});
map.showItems([myAnnotation]);
map.region = myRegion;
</script>
Now I want to:
• Show a walking route between two points
• Include waypoints on the route
Could someone show the code that would achieve this? Once I can see an example I know I'll get it ;-)
Ok, so I've found a solution to this so sharing it here for the benefit of others.
Let's start by saying Apple's MapKit JS doesn't appear to have a waypoints option as offered by Google Maps API - so the way around that is to create a map that stores the markers in an array and then routes from one to the next. The code stores the location of the last waypoint in a variable, and doesn't bother to draw a route to the last waypoint if this is the first one in the array (obviously).
<script>
// Initiallise MapKit - you'll need your own long-lived token for this
mapkit.init({
authorizationCallback: function(done) {
done('[MY-TOKEN]');
}
});
// Function to draw the route once MapKit has returned a response
function directionHandler(error, data) {
data["routes"].forEach(function(route, routeIdx) {
if (routeIdx !== 0) { return; }
overlays = [];
route['path'].forEach(function(path) {
// This styles the line drawn on the map
let overlayStyle = new mapkit.Style({
lineWidth: 3,
strokeColor: "#9b6bcc"
});
let overlay = new mapkit.PolylineOverlay(path, {
style: overlayStyle
});
overlays.push(overlay);
});
map.addOverlays(overlays);
});
}
// This asks MapKit for directions and when it gets a response sends it to directionHandler
function computeDirections(origin,destination) {
let directionsOptions = {
origin: origin,
destination: destination,
transportType: mapkit.Directions.Transport.Walking
};
directions.route(directionsOptions, directionHandler);
}
// This sets the initial region, but is overridden when all points have been potted to automatically set the bounds
var myRegion = new mapkit.CoordinateRegion(
new mapkit.Coordinate(55.9496320, -3.1866360),
new mapkit.CoordinateSpan(0.05, 0.05)
);
var map = new mapkit.Map("map");
map.region = myRegion;
var myAnnotations = [];
// lastWaypoint variable is 'unset' initially so the map doesn't try and find a route to the lastWaypoint for the first point of the route
var lastWaypoint = "unset";
var directions = new mapkit.Directions();
// Array of co-ordinates and label for marker
waypoints = [
{name:'Sofi’s Bar',lat:55.9746308,lon:-3.1722282},
{name:'TThe Roseleaf Cafe',lat:55.975992,lon:-3.173474},
{name:'Hemingway’s',lat:55.9763631,lon:-3.1706564},
{name:'Teuchter’s Landing',lat:55.9774693,lon:-3.1713826},
{name:'The King’s Wark',lat:55.9761425,lon:-3.1695419},
{name:'Malt and Hops',lat:55.975885,lon:-3.1698957},
{name:'The Carrier’s Quarters',lat:55.9760813,lon:-3.1685323},
{name:'Noble’s',lat:55.974905,lon:-3.16714},
{name:'The Fly Half',lat:55.9747906,lon:-3.1674496},
{name:'Port O’ Leith',lat:55.974596,lon:-3.167525}
];
// Loop through the array and create marker for each
waypoints.forEach(function(data) {
var myAnnotation = new mapkit.MarkerAnnotation(new mapkit.Coordinate(data['lat'],data['lon']), {
color: "#9b6bcc",
title: data['name']
});
myAnnotations.push(myAnnotation);
// As long as this isn't the first point on the route, draw a route back to the last point
if(lastWaypoint!="unset") {
computeDirections(lastWaypoint,new mapkit.Coordinate(data['lat'],data['lon']));
}
lastWaypoint = new mapkit.Coordinate(data['lat'],data['lon']);
});
map.showItems(myAnnotations);
</script>
This map is for a pub crawl around Leith, so the trasportType is 'Walking', but change that to 'Automobile' if you so wish.
With credit to Vasile whose MapKit JS Demo (https://github.com/vasile/mapkit-js-demo) helped me understand a lot more about the options.
Related
I have a web application, where a user can switch between some 160-ish layers. Most of them are Feature Layers, but some are of type ArcGISDynamicMapServiceLayer.
I need to be able to query those layers the same as I do with FeatureLayers: by clicking on any point on the map and displaying an infowindow.
This is my code so far (removed some bits for clarity):
executeQueryTask: function(evt, scope) {
//"this" is the map object in this context, so we pass in the scope from the caller,
//which will enable us to call the layer and map object and all the other precious widget properties
scope.map.graphics.clear();
scope.map.infoWindow.hide();
//we create a new Circle and set its center at the mappoint. The radius will be 20 meters
//default unit is meters.
var circle = new Circle({
/*...*/
});
// draw the circle to the map:
var circleGraphic = new Graphic(circle, /*...*/));
scope.map.graphics.add(circleGraphic);
var queryTask = new QueryTask(scope.layer.layer.url + "/" + scope.layer.layer.visibleLayers[0]);
var query = new Query();
query.returnGeometry = true;
query.outFields = ["*"];
query.geometry = circle.getExtent();
var infoTemplate = new InfoTemplate().setTitle("");
queryTask.execute(query, function(resultSet) {
array.forEach(resultSet.features, function(feature) {
var graphic = feature;
graphic.setSymbol(/*...*/));
//Set the infoTemplate.
// graphic.setInfoTemplate(infoTemplate);
//Add graphic to the map graphics layer.
scope.map.infoWindow.setContent(graphic.attributes);
scope.map.infoWindow.show(evt.mapPoint, scope.map.getInfoWindowAnchor(evt.screenPoint));
scope.map.graphics.add(graphic);
});
});
},
The key point is insise the queryTask.execute callback. If I uncomment and use graphic.setInfoTemplate(infoTemplate); the result is colored and upon a second click an infoWindow pops up.
There are 2 issues with this approach:
2 clicks are needed
I am unable to click on PolyLines and Points twice, so no infowindow pops up here.
This is why I added a circle to get a 100m buffer in radius to my click. Now I want to immediatly return an infoWindow.
At this point I'm struggeling to successfully create an Info Window, which is immediately displayed.
Currently the line scope.map.infoWindow.setContent(graphic.attributes); throws an error Failed to execute 'appendChild' on 'Node': parameter 1 is not of type 'Node'.
How can I create that Info Window?
I found a suitable approach, which leaves room for improvements. But this is for another iteration.
//create a new FeatureLayer object
var featureLayer = new FeatureLayer(scope.layer.layer.url + "/" + scope.layer.layer.visibleLayers[0], {
mode: FeatureLayer.MODE_SELECTION,
infoTemplate: new InfoTemplate("Attributes", "${*}"),
outFields: ["*"]
});
//we create a new Circle and set its center at the mappoint. The radius will be 20 meters
//default unit is meters.
var circle = new Circle({/*...*/});
// draw the circle to the map:
var circleGraphic = new Graphic(circle, /*...*/));
scope.map.graphics.add(circleGraphic);
var lQuery = new Query();
lQuery.returnGeometry = true;
lQuery.geometry = circle.getExtent();
featureLayer.queryFeatures(lQuery, function(results) {
array.forEach(results.features, function(feature) {
var graphic = feature;
graphic.setSymbol(/*...*/));
//now that we have the feature, we need to select it
var selectionQuery = new Query();
selectionQuery.geometry = feature.geometry;
featureLayer.selectFeatures(selectionQuery, FeatureLayer.SELECTION_NEW)
.then(function(selectedFeatures) {
console.info("selection complete", selectedFeatures);
if (!selectedFeatures.length) {
return;
}
scope.map.infoWindow.setFeatures(selectedFeatures);
scope.map.infoWindow.show(evt.mapPoint, "upperright");
});
});
});
The change here is, that we are no longer using a QueryTask, but create a new FeatureLayer object in selection mode, using the url and id of the visible layer.
The second noteworthy change is, that we no longer set the content of the infoWindow, but instead set selected features using infoWindow.setFeatures(selectedFeatures). Setting the content of an infoWindow, but not selecting features, hides the action list of the info window, this hinders you to zoom to an object or perform other custom operations.
In addition, this enables you( or me ) to view multiple results in the infoWindow, instead of just one.
I'm working hard on a meteor App which goal is to dynamically display on a google map the path of a vehicle, for example a boat on the sea.
Now, I see this library called gmaps.js, and since it is available on npm (just like google maps api) I decide to use this as a solution to draw of the map.
So, I have one page that add a geographic position (/runs/send) in the database each time I click on a button (this is enougth for testing). Then, on my other page (/runs/show) the goal is to get that data from mongo and prompt it dynamically on the map (meaning, if I add data by pressing the button, I'll see the new path appear on the map). Here is what the code looks like for now :
import { Template } from 'meteor/templating';
import {Tracker} from 'meteor/tracker';
import { Meteor } from 'meteor/meteor'
import gmaps from 'gmaps';
import google_maps from 'google-maps';
import {Mongo} from 'meteor/mongo';
import {Run} from './run.js';
import './methods.js';
Template.runs_show.helpers({
all() {
return Run.find({});
}
});
Template.runs_show.onCreated(function() {
var self = this;
self.autorun(function() {
self.subscribe('allRuns',function() {
google_maps.KEY = "MY API KEY";
google_maps.load(function(google){
console.log("Google Maps loaded");
// this creates a new map
var map = new gmaps({
el: '#map-gmaps',
lat: -12.043333,
lng: -77.028333
});
// for now , the data is on the run with {"maxSpeed" : "75"}
var dada = Run.findOne({"maxSpeed" : "75"});
// path look like [[x1,y1],[x2,y2]]
var path = dada.positions;
// this draws a red line on the map following the points defined in path
map.drawPolyline({
path: path,
strokeColor: '#FC291C',
strokeOpacity: 0.85,
strokeWeight: 6
});
});
});
});
});
So, as you can see, I put my onCreated function in a autorun block, and the data i'm using is from a database, so it's a cursor, so it should be reactive as well.
With a reactive data, inside a reactive block of code (thanks autorun)? expected to see a new line appear on my screen when I press "send" in my second page (this page just add a new set of [x,y] to the run.positions), but.... Nothing ! In fact, If I reload the page manually, the new line appears, of course, but wellll... That's not what I wanted to be honest...
So that's it! any idea what is missing in order to have some true reactivity?
EDIT :
This code works partially : the first time I load the page, the console.log(map) gives a undefined, but I just need to reload once, and then the page will work exactly as intended, showing what I want dynamically. However, one single code reload, and then, again, the console.log(map) gives undefined, and I need a new F5.... Any idea on why it does that / how to solve it?
Template.runs_show.onCreated(function() {
google_maps.KEY = "MY API KEY";
google_maps.load(function(google){
var map = new gmaps({
el: '#map-gmaps',
lat: -12.043333,
lng: -77.028333
});
// with that, I can use the map in the onRendered
Template.runs_show.map = map;
});
console.log(Template.runs_show);
});
Template.runs_show.onRendered(function() {
var self = this;
self.autorun(function() {
self.subscribe('allRuns',function() {
Tracker.autorun(function(){
var map = Template.runs_show.map;
console.log(map);
var dada = Run.findOne({"maxSpeed" : "75"});
var path = dada.positions;
map.drawPolyline({
path: path,
strokeColor: '#FC291C',
strokeOpacity: 0.85,
strokeWeight: 6
});
// seems to not be necesary
//map.refresh();
});
});
});
});
(in this new code, I just create the map in the onCreated, when the gmaps is loaded, and then, I make all the drawing in the onRendered. Btw, I used Template.xxx.map to transmit data between onCreated and onRendered, is that what i'm supposed to do?)
Try using nested templates for this. So that your wrapper template subscribes to data and only renders nested template when subscriptions is ready:
//wrapper template that manages subscription
Template.wrapper.onCreated(function() {
this.subscribe('allRuns');
});
Template.runs_show.onRendered(function() {
google_maps.KEY = "MY API KEY";
google_maps.load(function(google){
var map = new gmaps({
el: '#map-gmaps',
lat: -12.043333,
lng: -77.028333
});
Tracker.autorun(function() {
// Run.find will re-trigger this whole autorun block
// if any of the elements of the collection gets changed or
// element gets created or deleted from collection
// thus making this block reactive to data changes
// this will also work with findOne in case you only want to
// one run only
var runs = Run.find({"maxSpeed" : "75"}).fetch();
// map.clear() or something like that to remove all polylines
// before re-rendering them
runs.forEach(function(run){
map.drawPolyline({
path : path,
strokeColor : '#FC291C',
strokeOpacity : 0.85,
strokeWeight : 6
});
});
});
// ^^ this is pretty dumb and re-renders all items every time
// something more intelligent will only re-render actually
// changed items and don't touch those who didn't change but
// that will require a slightly more complex logic
});
});
wrapper.html:
<template name="wrapper">
{{#if Template.subscriptionsReady }}
{{> runs_show }}
{{/if}}
</template>
P.S. this is mostly pseudo code since I never tested it, so just use it as a guide
Seems the issue is that the subscribe callback is not a reactive context. Try doing what worked for others here and here, as well as putting the tracking in your onRendered.
Template.templateName.onRendered(function() {
var self = this;
self.autorun(function() {
self.subscribe('allRuns',function() {
Tracker.autorun(function(){
...
}
})
})
})
I wrote AngularJS application which shows Google, Yandex and Gis maps for specific coordinates, obtained from JSON object. JSON may contains [0...n] objects.
And it is well works with Google Maps API, but not with Yandex/Gis Maps API.
Yandex Maps:
// DOESN'T WORK
$scope.yandexMaps = function (city) {
ymaps.ready(init);
function init() {
$scope.mapContainer = document.createElement('div');
$scope.mapContainer.className = 'mapCon';
$scope.yandexMap = new ymaps.Map($scope.mapContainer, {
center: [city.lat, city.lng],
zoom: 12
});
$scope.placemark = new ymaps.Placemark([city.lat, city.lng]);
$scope.yandexMap.geoObjects.add($scope.placemark);
return $scope.mapContainer; //I NEED TO GET IT !
}
//BUT I NEED TO GET IT THERE !
};
Gis Maps:
// DOESN'T WORK
$scope.gisMaps = function (city) {
DG.then(init);
function init() {
$scope.mapContainer = document.createElement('div');
$scope.mapContainer.className = 'mapCon';
$scope.gisMap = DG.map($scope.mapContainer, {
center: [city.lat, city.lng],
zoom: 13
});
DG.marker([city.lat, city.lng]).addTo($scope.gisMap);
return $scope.mapContainer; //I NEED TO GET IT !
}
//BUT I NEED TO GET IT THERE !
};
The problem is that ymaps.ready(init); and DG.then(init); call init() functions only after they will be checked for ready map state. And I can't return $scope.mapContainer in both cases because they are in nested functions.
I tried to create global variable above init() functions and assign to this $scope.mapContainer, but when I return this global value in parent below init() function it is not visible. It is visible only in nested function.
YOU CAN SEE working JSFiddle here: https://jsfiddle.net/oxpgkLhj/2/
Please, open console to see errors
If you are commenting this part at the end of code:
//adding yandex map to common container
$scope.yandexMap = $scope.yandexMap(value);
$scope.yandexMap.id = "yandexMapContainer" + i;
angular.element($scope.container).append($scope.yandexMap);
and this:
//adding gis map to common container
$scope.gisMap = $scope.gisMaps(value);
$scope.gisMap.id = "gisMapContainer" + i;
angular.element($scope.container).append($scope.gisMap);
you will see perfectly worked Google Maps. I want to see Yandex and Gis Maps exactly the same. Please help...
I have a page that works well it loads a Bing Map and creates a layer which is then filled with Polygons. I then need to reload the JSON data that makes the polygons and this again works, however the data is then added to another layer so appears over the top. I have tried to delete the layer, clear the layer etc etc but nothing seems to work.
Any idea please.
This is the function that does it all...
function AddData() {
dataLayer = new Microsoft.Maps.Layer();
Microsoft.Maps.loadModule('Microsoft.Maps.GeoJson', function () {
var featureCollection = Microsoft.Maps.GeoJson.read(json, {
polygonOptions: {
strokeColor: 'LightSkyBlue',
strokeThickness: 2
}
});
for (var i = 0; i < featureCollection.length; i++) {
var fillColour = featureCollection[i].metadata.FillColor;
featureCollection[i].setOptions({ fillColor: fillColour });
Microsoft.Maps.Events.addHandler(featureCollection[i], 'click', displayClickBox);
Microsoft.Maps.Events.addHandler(featureCollection[i], 'mouseover', displayMouseOverBox);
Microsoft.Maps.Events.addHandler(featureCollection[i],'mouseout', displayMouseOut);
dataLayer.add(featureCollection[i], 0);
}
map.layers.insert(dataLayer);
});
}
var getJson = function () {
var onContentComplete = function (response) {
//Load the JSON data into the local variable for use latter in the project...
json = response.data;
//load the map now that we have the polygon data...
AddData();
};
var onError = function (reason) {
//An error has occured so display a message to the user...
$scope.error = "Server communication error please try again...";
//Log the error to the console for admin debug...
console.log(reason.data);
};
//Load the JSON for the map polygons into memory ready for display...
$http.get("../JSON/MapData.json")
.then(onContentComplete, onError);
}
As I have said I have tried to clear the layer first using
dataLayer.clear();
But that seems to do nothing.
Help please as I have been working at this for hours now.
Thanks
Cliff.
By the sounds of things you want all data to render in a single layer, but instead it is rendering two or more layers. This happens because you are creating a new instance of dataLayer every time the AddData function is called. Additionally, this overwrites the local variable for dataLayer and thus you lose the reference to the original layer that you are trying to clear/delete. If you want to clear or delete the layer, do that before initializing the new layer. Try adding the following to the top of your AddData function:
if(dataLayer){
map.layers.remove(dataLayer);
}
Alternatively, reuse the layer by clearing it if it exists of or creating it if it doesn't:
if(dataLayer){
dataLayer.clear();
}else{
dataLayer = new Microsoft.Maps.Layer();
}
I'm developing an Android application containing a Webview. The HTML corresponding to the webview contains mainly JavaScript code ; it retrieves a map of a building from a Geoserver. I use Leaflet to display the different layers. Each base layer is a floor.
I'm adding 2 overlays to the base layers from Android ; one is a heatmap, the other is a set of markers representing the locations of some sensors. Thanks to a JavaScript interface in Android, the JavaScript code can get datasets created from Android.
It works when I display only ONE of the overlays. As soon as I want to display both overlays, or after I displayed an overlay, hid it, and display the other, the application crashes. I have different errors according to the overlay added first.
Something really strange : it only happens with my tablet (which is very cheap), not with the AVD.
Here is my JavaScript code :
<script type="text/javascript" charset="utf-8">
var map, baseLayers, heatmapLayer, sensorsLayer;
var ground_floor = new L.tileLayer.wms('http://192.168.1.16/geoserver/wms',
{
layers: 'ground_floor',
format: 'image/png'
});
var first_floor = new L.tileLayer.wms('http://192.168.1.16/geoserver/wms',
{
layers: 'first_floor',
format: 'image/png'
});
var second_floor = new L.tileLayer.wms('http://192.168.1.16/geoserver/wms',
{
layers: 'second_floor',
format: 'image/png'
});
var current_floor = ground_floor;
baseLayers = {
"Ground floor": ground_floor,
"First floor": first_floor,
"Second floor": second_floor
};
map = new L.Map('map', {
center: new L.LatLng(-45, 170),
zoom: 30,
layers: [ground_floor],
crs: L.CRS.EPSG900913
}).setView([-45.8668664, 170.5185323], 31);
var control = L.control.layers(baseLayers).addTo(map);
map.on('baselayerchange', onBaseLayerChanged);
// Called when the base layer (meaning the floor) is changed
function onBaseLayerChanged(event) {
// Update the current floor
if (event.layer == ground_floor)
current_floor = ground_floor;
else if (event.layer == first_floor)
current_floor = first_floor;
else if (event.layer == second_floor)
current_floor = second_floor;
else
Android.debug("Wrong base layer");
Android.debug("yo1");
// Update the heatmap and sensors' location
// if they are displayed
if (map.hasLayer(heatmapLayer)) {
removeHeatmap();
addHeatmap();
}
Android.debug("yo2");
if (map.hasLayer(sensorsLayer)) {
Android.debug("yo3");
removeSensors();
addSensors();
Android.debug("yo4");
}
}
function addHeatmap() {
heatmapLayer = L.TileLayer.heatMap({
// radius could be absolute or relative
// absolute: radius in meters, relative: radius in pixels
radius: { value: 5, absolute: true },
opacity: 0.8,
gradient: {
0.45: "rgb(0,0,255)",
0.55: "rgb(0,255,255)",
0.65: "rgb(0,255,0)",
0.95: "yellow",
1.0: "rgb(255,0,0)"
}
});
var dataSet;
if (current_floor == ground_floor)
dataSet = JSON.parse(Android.getDataSetGroundFloor());
else if (current_floor == first_floor)
dataSet = JSON.parse(Android.getDataSetFirstFloor());
else if (current_floor == second_floor)
dataSet = JSON.parse(Android.getDataSetSecondFloor());
else
Android.debug("Error getDataSet : wrong floor");
heatmapLayer.setData(dataSet.data);
control.addOverlay(heatmapLayer, "Heatmap");
heatmapLayer.addTo(map);
}
function removeHeatmap() {
control.removeLayer(heatmapLayer);
map.removeLayer(heatmapLayer);
}
function addSensors() {
var sLat, sLon;
var markers = new Array();
if (current_floor == ground_floor) {
sLat = JSON.parse(Android.getLatitudesGroundFloor());
sLon = JSON.parse(Android.getLongitudesGroundFloor());
}
else if (current_floor == first_floor) {
sLat = JSON.parse(Android.getLatitudesFirstFloor());
sLon = JSON.parse(Android.getLongitudesFirstFloor());
}
else if (current_floor == second_floor) {
sLat = JSON.parse(Android.getLatitudesSecondFloor());
sLon = JSON.parse(Android.getLongitudesSecondFloor());
}
else
Android.debug("Error getCoordinates : wrong floor");
for (i=0; i<sLat.length && i<sLon.length; i++) {
var marker = L.marker([sLat[i],sLon[i]]);
markers.push(marker);
}
sensorsLayer = L.layerGroup(markers);
control.addOverlay(sensorsLayer, "Sensors");
sensorsLayer.addTo(map);
}
function removeSensors() {
control.removeLayer(sensorsLayer);
map.removeLayer(sensorsLayer);
}
</script>
If I add the heatmap first, and then the sensors I have a NullPointerException on "sensorsLatFloor" of the following code :
public JSONArray getLatitudesGroundFloor() {
if (!isSensorsQueryDone())
new SelectSensorsLocationATask(SensorsFragment.this).execute();
// Wait for the end of the query
while (!querySuccessful) {}
JSONArray latJSON = null;
latJSON = new JSONArray(sensorsLatGroundFloor);
return latJSON;
}
This is for the ground floor but it does the same for every floor. "sensorsLatGroundFloor" is an ArrayList filled by the AsyncTask SelectSensorsLocationATask after a query in the local database. The code works, since it works when I only want to display sensors.
When I display the sensors first, and then the heatmap, the app crashes and I have the following error in the LogCat :
JNI ERROR (app bug): accessed staled weak global reference 0xffffffff (index 65535 in a table of size 8)
VM aborting
Fatal signal 11 (SIGSEV) at 0xdeadd00d
This is very odd, because I don't manipulate JNI code at all...
Besides, I have another error, which must be very stupid but I don't figure out why it doesn't work. Have a look at this part of my JavaScript code :
// Called when the base layer (meaning the floor) is changed
function onBaseLayerChanged(event) {
// Update the current floor
if (event.layer == ground_floor)
current_floor = ground_floor;
else if (event.layer == first_floor)
current_floor = first_floor;
else if (event.layer == second_floor)
current_floor = second_floor;
else
Android.debug("Wrong base layer");
Android.debug("yo1");
// Update the heatmap and sensors' location
// if they are displayed
if (map.hasLayer(heatmapLayer)) {
removeHeatmap();
addHeatmap();
}
Android.debug("yo2");
if (map.hasLayer(sensorsLayer)) {
Android.debug("yo3");
removeSensors();
addSensors();
Android.debug("yo4");
}
}
This function is supposed to update the overlay(s) displayed when I switch the base layer (meaning the floor). It works with the heatmap, but not with the sensors... If the heatmap is not on the map, the function is finished, it doesn't even look at my second if. You can see my "Android.debug", it displays in the LogCat the message I put as parameter. Here, the LogCat only displays "yo1".
EDIT : I figured something out about this last error. The problem seems to be the Leaflet function "hasLayer". If the map has the layer indicated, it returns true. So in my mind, if it doesn't, it's supposed to return false... It makes sense. But instead of it, it makes the code bug, so the code after the function is ignored. Either I'm making a mistake that I don't see when I call it, or Leaflet made a useless function... So I must have done a silly mistake but I don't find it !
I hope I've been clear enough to let you understand my problems... Let me know if you need more Android code, although I don't think it would be helpful.
Thanking you in advance.
* PROBLEM FIXED *
The errors were caused by a problem of task synchronisation in the Android code. You can see that I wait for the end of the query with the variable "querySuccessful". Actually I was manipulating this variable from the AsyncTask only : I set it to false at the beginning and true at the end. Now I set it to true before I start the AsyncTask, and the application works, I have no more errors.
I still have some problems with the Leaflet function hasLayer ; it works when I switch the base layer once, sometimes twice, but then it stops working. But apparently it might be an error from Leaflet, which will be fixed in the next release.