LEAFLETJS Fetch all points along route between waypoints - javascript

[Duplicate] I am asking this question again as my original question was incorrectly closed by linking the question below to mine, but the provided solution doesn't answer my question.
Previously provided "solution": (Getting waypoints from leaflet routing machine) - This question does not answer my question as the answer only returns the waypoints that I have already provided, it doesn't return the points between the waypoints that comprise the route.
My original question is here: How can I fetch all points along route between waypoints?
I want to animate a marker along a route. I've been able to animate a marker successfully between waypoints but I want to fetch the points between two waypoints programmatically so that I can use a function to automatically set a route and animate along the route.
Two waypoints will be known, i.e. Point A and Point B. What I want is to be able to get all points between Point A and Point B automatically so that the marker is animated along the route instead of as the crow flies. I can animate the route successfully by entering the desired waypoints manually so I know that this will work if I can retrieve the points between the waypoints.
Here's my code so far:
// Draw route
var routeData = L.Routing.control({
router: L.Routing.mapbox(L.mapbox.accessToken,{
profile : 'mapbox/driving',
language: 'en',
}),
waypoints: [
L.latLng(52.501737, -2.119792),
L.latLng(52.498798, -2.102591)
],
lineOptions:{
styles: [
{
color: '#006a4e', opacity: 1, weight: 5
}
],
},
draggableWaypoints: false,
createMarker: function() { return false; },
show: false,
}).addTo(mymap);
var routeArray = new Array();
routeArray = routeData.getWaypoints();
console.log(routeArray);
// Draw animation
L.motion.polyline([[52.501737, -2.119792], [52.501267, -2.114707], [52.500313, -2.110361], [52.499243, -2.108751], [52.498596, -2.105886], [52.498812, -2.104953], [52.498798, -2.102591]], {
color: "transparent"
}, {
auto: true,
duration: 30000,
easing: L.Motion.Ease.easeInOutQuart
}, {
removeOnEnd: false,
showMarker: true,
icon: L.icon({iconUrl: 'marker.png', iconSize: [32,37]})
}).addTo(mymap);
The plan is to find the points between the waypoints, enter them into an array and explode the array within the polyline, like so:
var pointsArray = new Array(???);
L.motion.polyline(pointsArray,...
I just don't know how to get the data for pointsArray. I'm using Leaflet Routing Machine with MapBox data.
Any help?
I hope I've explained myself clearly enough.

Related

Leaflet Draw - Editing Polygon: Editing does not behave right

i am currently working on a project where a user should be able to edit zones (= polygons). I have created a class Zone which extends L.Polygon so I can create zones and add them to the map. The editing of a specific instance of Zone is triggered by a button, which rund the following code: specificZone.editable.enable()
This works so far and looks like this:
The problem I have, is that when dragging the edit points around, one corner point always behaves like an edge point: So it splits the corner like this:
The other points behave as intended. I hope this was somewhat understandable outlined.
I am thankful for any kind of help. Have a nice day :)
Kind regards
Luca
Edit:
My map gets created as follows:
A Vue Component Map.vue creates an instance of CustomMap.ts:
const customMap = new CustomMap(
document.getElementById("mapcontainer") as HTMLElement,
mapUrl,
mapParams.getData()
);
the mapUrl is the place where the map is stored and the mapParams are previously loaded from a server
In the constructor of CustomMap.ts a L.DrawMap gets created, then the map gets added to the map as an instance of CustomImageOverlay which creates a L.imageOverlay. After that the L.FeatureGroup for the CustomZone(s) gets created.
this.map = L.map(el, {
crs: L.CRS.Simple,
center: [0, 0],
zoom: 4,
minZoom: 2,
maxZoom: 7,
dragging: true,
maxBoundsViscosity: 0.5,
maxBounds: mapBounds,
zoomSnap: 0.25,
zoomDelta: 0.25,
attributionControl: false
});
this.imageOverlay = new CustomImageOverlay(
mapUrl,
mapBounds.getNorthEast(),
mapBounds.getSouthWest(),
{
opacity: 0.5,
interactive: true,
zIndex: -1
},
this.map
);
this.map.addLayer(this.imageOverlay.getLayer());
// Create a group for zones and add it to map
const zoneGroup = new L.FeatureGroup();
zoneGroup.addTo(this.map);
// init drawers
this.polygonDrawer = new L.Draw.Polygon(this.map);
// Layers to show/hide
const overlayLayers: L.Control.LayersObject = {
Zones: zoneGroup,
};
// Create Layer Control with the previous options
const layerControl = new L.Control.Layers(base, overlayLayers);
// Add to map
layerControl.addTo(this.map);
The CustomZone is created and added to the feature group and map.
In the constructor of the customZone the actual polygon gets created by super(latlng, options)
I hope this code gives you better insight :)

Two different panoid for the same photosphere Google Maps API JS

i would appreciate some help in understanding why there are two different panoId patterns on Google StreetView.
I'm initializing photospheres with the following JavaScript code:
function initialize() {
var pocetneKoordinate = new google.maps.LatLng(44.81856295912351, 20.455767810344696);
var pocetniPanoID = 'F:-By57yDKJr5M/WWfMHHWwYjI/AAAAAAAACXU/v1tk1TK02yEmBWGt2U4sMK1d_Uf3qdKmwCLIBGAYYCw';
var mapOptions = {
center: pocetneKoordinate,
zoom: 0
};
var map = new google.maps.Map(document.getElementById('panorama'),
mapOptions);
panorama = map.getStreetView();
var panoOptions = {
position: pocetneKoordinate,
pano: pocetniPanoID,
visible: true,
pov: {
heading: 41,
pitch: 0,
zoom:1,
},
clickToGo: false
};
panorama.setOptions(panoOptions);
// Create a StreetViewService object.
var streetviewService = new google.maps.StreetViewService();
}
Just in case I pasted something wrong, working JSFiddle https://jsfiddle.net/markovica/pcLjbmwk/
It all works great, but what confuses me is that I open the same panorama with two different PanoID strings, ex:
CAoSLEFGMVFpcE1EZGhWWFJzVzBTd0I4amlQOWtjdEJ3Z3MwVnYtNTZBbEJNRHBI
F:-By57yDKJr5M/WWfMHHWwYjI/AAAAAAAACXU/v1tk1TK02yEmBWGt2U4sMK1d_Uf3qdKmwCLIBGAYYCw
Besides that, on other linked photospheres, panorama.getPano() will return the pattern of the initial pano, and links are not exactly the same (Screenshot showing differences in links) - there are some slight differences which I suppose are due to my recent edits.
But why are there two panoIDs for the same panorama and why are they behaving slightly different?
The correct way of retrieving PanoID is by querying StreetView Publish API. It will give the first pattern.
I used the API explorer:
https://developers.google.com/apis-explorer/#p/streetviewpublish/v1/

How to pass string address to leaflet routing machine to get direction based on string address?

Well, I am using leaflet api and then I found a very nice supporting plugin called leaflet routing machine for showing address from A to B with nice route.
However, leaflet routing machine working fine with passing of latlng but not working with passing address so can anyone tell how it can work as I know property information on following link:
So routing waypoint have property called name but don;t know how to use it to provide address a and address B
Here is a code which shows new Zealand Auckland city....and trying to pass address but doesn't work
< script >
var mymap = L.map('mapid', {
center: [-36.85625, 174.76141],
zoom: 13
});
L.tileLayer('https://api.tiles.mapbox.com/v4/{id}/{z}/{x}/{y}.png?access_token=pk.yourkey', {
attribution: 'Log Sample',
id: 'mapbox.streets'
}).addTo(mymap);
//L.Control.geocoder().addTo(mymap);
L.Routing.control({
waypoints: [
//L.latLng(-36.87178, 174.753),
//L.latLng(-36.84514, 174.76493)
L.name("12 Morning Star place, Auckland"),
L.name("198 Dominion road, Mount Roskill, Auckland")
],
routeWhileDragging: false
}).addTo(mymap); < /script>
As far as remember you can pass L.Routing.Waypoint object to waypoints as well.
So, your code would look like:
....
var geocoder = L.Control.Geocoder.nominatim()
L.Routing.control({
geocoder: geocoder,
waypoints: [
//L.latLng(-36.87178, 174.753),
//L.latLng(-36.84514, 174.76493)
L.Routing.waypoint(null,"12 Morning Star place, Auckland"),
L.Routing.waypoint(null,"198 Dominion road, Mount Roskill, Auckland")
],
routeWhileDragging: false,
}).addTo(mymap);
But this again doesn't geocode it. Instead just populates waypoints textboxes. You still need to hit enter (or trigger it by js) on each of the boxes to run geocoder.
Another option would be manually grabbing data from geocoder and create L.Routing.Waypoint or L.LatLng object before setting waypoints
geocoder.geocode('Montreal', function(a, b) {
// depending on geocoder results may be either in a or b
console.log(a);
// choose the best result here. probably the first one in array
// create waypoint object
var wpt = L.Routing.waypoint(L.latLng(lat, lng), name)
waypoints.push(wpt);
})
...
// setting waypoints
routingControl.setWaypoints(waypoints);
Update to cover question in comments
The custom markers with popup could be added via L.Routing.Plan. Your L.Routing.control object could be initialized like so:
var geocoder = L.Control.Geocoder.nominatim(),
waypoints = [], // can be populated later
routeControl = L.Routing.control({
plan: L.Routing.plan(waypoints, {
createMarker: function(i, wp) {
return L.marker(wp.latLng, {
draggable: true
}).bindPopup("Some data for popup");
},
geocoder: geocoder,
routeWhileDragging: false,
})
}).addTo(map);

How do I access and hide markers created by loadGeoJson()?

I am loading custom coordinates into my map application via JSON. I have been able to find out how to color code the markers based on feature properties, but one of my next steps will be to create filters to show or hide markers based on the properties.
My code starts like this:
var map;
var infowindow = new google.maps.InfoWindow();
function initialize()
{
var mapCanvas = document.getElementById('map');
var mapOptions = {
mapTypeId: google.maps.MapTypeId.ROADMAP
}
map = new google.maps.Map(mapCanvas, mapOptions);
map.data.loadGeoJson('/map_json.php', null, SetBounds);
map.data.setStyle(function(feature) {
var color = 'FF0000';
var symbol = '%E2%80%A2'; // dot
// color selection code here [...]
return /** #type {google.maps.Data.StyleOptions} */ {
icon: 'http://chart.apis.google.com/chart?chst=d_map_pin_letter_withshadow&chld=' + symbol + '|' + color
};
}
I already found how I can access the imported data through a jquery autocomplete search:
$(input).autocomplete({
minLength: 0,
source: function(request, response) {
data = [];
map.data.forEach(function(feature)
{
var str = request.term.toUpperCase();
if (String(feature.getProperty('name')).toUpperCase().indexOf(str) > -1)
{
data.push({id: feature.getProperty('id'), name: feature.getProperty('name'), address: feature.getProperty('address')});
}
});
response(data);
},
select: function(event, ui)
{
map.data.forEach(function(feature)
{
if (feature.getProperty('id') == ui.item.id)
{
var content = GetContent(feature);
infowindow.setContent(content);
infowindow.setPosition(feature.getGeometry().get());
infowindow.setOptions({pixelOffset: new google.maps.Size(0, -34)});
infowindow.open(map);
// zoom in
map.setZoom(15);
map.panTo(feature.getGeometry().get());
return false;
}
});
}
})
.autocomplete().data('uiAutocomplete')._renderItem = function(ul, item)
{
return $('<li>')
.append('<a>' + item.name + ' (ID: ' + item.id + ')<br>' + item.address + '</a>')
.appendTo(ul)
};
So using this same principle to run my filters is not a problem.
The problem is that I have not found a way yet to access the visible markers based on the feature information that I have in map.data.
All the examples I found so far are based on the principle of manually adding markers and storing them in an array to access later, e.g.:
var marker = new google.maps.Marker({
position: myLatLng,
map: map,
title: 'Hello World!'
});
But I don't have that - I load the entire set of data using getGeoJson().
How can I access the marker and manipulate it (e.g. hide it or show it) based on the information I can access using map.data.forEach()?
--- Update ---
Here are more details on the project.
The map will markers that are generated from a list of customers. The customers have different categories and properties, so a typical entry form the GeoJSON string would look like this:
{"type":"Feature","geometry":{"type":"Point","coordinates":[0,0]},"properties":{"name":"Customer 1","id":"1001","address":"1234 Test Street, Test City, TX 12345, USA","category":"vendor","active":1}}
Also on the map is a filter box with checkboxes that are checked by default. Clicking any of them will run the filtering code that should hide or remove the markers that are associated with any customers that match that filter.
So if I disable the checkbox that filters "inactive", then only customers with the property "active":1 will remain on the map. If I disable the checkbox that filters "vendors", then all customers with the category "vendor" will be hidden.
Checking the checkboxes again later will undo the hiding of these entries.
What I have found in my research is a lot of mentioning of markers, but ONLY if they are added manually - not via GeoJSON import.
I can see a few potential solutions to my problem - I could ignore the GeoJSON format and instead import the client list into jQuery manually and parse it from there into markers that then go into an array. But then why use the GeoJSON format at all?
My current solution of using map.data.setStyle() (see comment) seems to work and do the job. But I am curious if there isn't another more direct way.
I figured, the filter function would go through all data (map.data.forEach()) to locate any items that should be hidden based on the filters, and then each item would communicate to its associated marker that the marker needs to be hidden. But it is this association that I have not been able to figure out so far.
When I loop through all features (map.data.forEach()), I have access to the data I uploaded, but not to the markers that were placed as a result of the import.
My question is if there is a direct way to access the marker from the feature.
I hope this is clearer now.
--- Update ---
I created a very simple jsfiddle for it:
http://jsfiddle.net/semmelbroesel/9bv68ngp/
This is the concept I want to achieve, and it works as is. My only question is if there is another way to achieve the same results by directly accessing the placed markers instead of using setStyle() to hide/show them.
You don't need to use forEach, since setStyle does already traverse the Features.
If you declare the styling function as:
map.data.setStyle(function(feature) {
var color = 'FF0000';
var symbol = '%E2%80%A2'; // dot
return /** #type {google.maps.Data.StyleOptions} */ {
visible: feature.getProperty('active'), // this links visibility to feature property
icon: 'http://chart.apis.google.com/chart?chst=d_map_pin_letter_withshadow&chld=' + symbol + '|' + color
};
});
You don't need to call the method again, since the style gets bound to the feature property. Setting the active property to false will propagate to the marker style seamlessly.
After that, you can make your filters like (rough example)
var setFilter = function(property, value) {
map.data.forEach(function(feature) {
feature.setProperty('active', feature.getProperty(property) === value);
});
};
and calling for example setFilter('name','John');
Again, this is a rough example. I'd rather implement the filtering method on the google.maps.Data prototype, but this should point you in the right direction.

How to display markers (huge numbers) on a map which has been logically divided into segments?

What i have done so far:
i'm developing an application where i have to display more than(50K) points/Markers on the Navteq map divided into different segments.
for example: if i have 50K points i will divide all points into different segments.
if i divide 50K points into 50 segments each segment would have 1000 points (may not be 50 segments , it may depend).
right now it is working but it takes long time and hangs to render all the points on the MAP.so that i would like to perform segmentation displaying to display only few points with clustering.
so that i can get an idea of how the segment will look like.
but the problem here is i should only perform the clustering based on the segments.otherwise points from different segments willbe mixed together and displayed
as single unit and that conveys the wrong information to the user.
so here my question is: is it possible to perform the clustering based on the segment. so that only points from same segment will be clustered.
Note: if this is not possible, i would like to use Latest version of here-maps 2.5.3 (Asynchronous) may reduce some time while loading, so that i would like to use indexing functionality also while rendering the points
to improve the rendering time using nokia.maps.clustering.Index class.
i studied that indexing would reduce the time while rendering the points/markers on map. does it help in my case? could anybody please suggest how to perform indexing ?
This is the code with which i'm displaying points on map:
function displayAllLightPoints(arrLightPointCoordinats, totalLightPoints,
selectedSegmentId, totalSegmentsCount,segmentColorcode)
{
var MyTheme1 = function () {
};
segmentColorcode = segmentColorcode.substring(2,segmentColorcode.length-1);
MyTheme1.prototype.getNoisePresentation = function (dataPoint) {
var markerLightPoint = new nokia.maps.map.Marker(dataPoint, {
icon: new nokia.maps.gfx.BitmapImage("..//Images//Lightpoint//" +
segmentColorcode + ".png"),
anchor: {
x: 12,
y: 12
}
});
return markerLightPoint;
};
MyTheme1.prototype.getClusterPresentation = function (data) {
var markerLightPoint = new
nokia.maps.map.StandardMarker(data.getBounds().getCenter(), {
icon: new nokia.maps.gfx.BitmapImage("..//Images//
Segment/" + segmentColorcode + ".png", null, 66, 65),
text: data.getSize(),
zIndex: 2,
anchor: {
x: 12,
y: 12
}
});
return markerLightPoint;
};
var ClusterProvider = nokia.maps.clustering.ClusterProvider,
theme = new MyTheme1(),
clusterProvider = new ClusterProvider(map, {
eps: 0.00000000001,
minPts: 1000000,
strategy: nokia.maps.clustering.ClusterProvider.
STRATEGY_DENSITY_BASED,
theme: theme,
dataPoints: []
});
var lightpointsDataSet1 = new Array();
for (var i = 0; i < totalLightPoints; i++) {
lightpointsDataSet1[i] = { latitude: arrLightPointCoordinats[i][0],
longitude: arrLightPointCoordinats[i][1], title:
'LightPoint ' + (i + 1) };
}
clusterProvider.addAll(lightpointsDataSet1);
clusterProvider.cluster();
}
To deal with a very large (50K+) data set , I would do all the heavy number crunching server side and send over a new JSON response whenever the map is updated. Something like the HTML page described here
The key section of the code is the ZoomObserver:
var zoomObserver = function (obj, key, newValue, oldValue) {
zoom = newValue;
if (zoom < 7)
{ zoom = 7;}
if (zoom > 16)
{ zoom = 16;}
// Define the XML filename to read that contains the marker data
placeMarkersOnMaps('http://api.maps.nokia.com/downloads/java-me/cluster/'+ zoom + '.xml'
+ '?lat1=' + map.getViewBounds().topLeft.latitude
+ '&lng1='+ map.getViewBounds().topLeft.longitude
+ '&lat2='+ map.getViewBounds().bottomRight.latitude
+ '&lng2='+ map.getViewBounds().bottomRight.longitude);
};
map.addObserver("zoomLevel", zoomObserver );
Where the REST service returns a "well-known" data format which can be used to add markers and clusters to the map.
Now assuming you have two massive data sets you could make two requests to different endpoints, or somehow distinguish which cluster of data belongs to which so that you would just be returning information of the form:
{latitude':51.761,'longitude':14.33128,'value':102091},
i.e. using the DataPoint standard (which means you could use a heat map as well.
Of course, what I'm not showing here is the back-end functionality to cluster in the first place - but this leaves the client (and the API) to do what it does best displaying data, not number crunching.

Categories