I have made a API meant to fetch Electrical Vehicles Charging Points on a map and create a route.
The route should automatically add charging points if the vehicle's capacity is not enough to finish the trip.
I fetch a Json and create an array of Marker objects. This works fine.
I then pass it through a loop that should splice all markers too far from the starting point to be reached and then another loop selecting the marker closest to destination.
For a reason I do not understand, though the array seems to have been trimed correctly, the result is a marker just next to destination and totally out of range from the starting point.
I am using the following libraries from Google: Directions, Geocoding, Maps Javascript...
I hope someone can help me because I am totally stuck here.
EDIT (further explanations:
The program is meant to add a charging stopover when the travel gets longer than the vehicle capacity (which is is 220 km.)
All the eligible stopovers are shown on the map and pushed in an array (as marker objects).
The function first splice all the stopovers further than 220 km. from the array, then in another loop I chose the stopover closest to destination.
But, if you look closer at the route you will see the charging stopover (point B) is actually 550 km. from departure and 2 km. from arrival.
This Marker should have been spliced and not be in the array anymore.
function selectMarkerClosestToDestination(vehicle) {
//Selecting the closest marker to destination as long as it is not out of the vehicle capacity range
//CURRENTLY BUGGED
let waypoints = chargingPointsMarkers;
for (let x = waypoints.length -1; x > 0; x--) {
if(calculateDistance(waypoints[x], start) > (vehicle.status*vehicle.consumption)){
console.log(calculateDistance(waypoints[x], start))
console.log(vehicle.status*vehicle.consumption)
waypoints.splice(x, 1)
console.log(waypoints)
}
}
console.log(waypoints)
for (let x = waypoints.length - 1; x > 0; x--) {
if (calculateDistance(waypoints[x], end) > (calculateDistance(waypoints[x-1], end))) {
waypoints.splice(x, 1);
} else {
waypoints.splice(x - 1, 1);
}
}
console.log(waypoints)
return waypoints[0];
}
function calculateDistance(p1, p2) {
//Uses the Google geometry library to calculate distance between two Markers
let a = p1.getPosition();
let b = p2.getPosition();
let distance = (google.maps.geometry.spherical.computeDistanceBetween(a, b) / 1000).toFixed(2);
return distance;
}
This is the full code (code snippet):
let map;
let mapCenter = { lat: 59.428, lng: 24.76};
let start;
let end;
let chargingPointsMarkers = [];
let markerArray = [];
let stopoverMarkers = []
let vehicle1 = {capacity: 33, status: 33, consumption: 6.6666} //1KW = 6.6666 Km; Capacity in KM = status*consumption;
function initMap(listener) {
//Create the map, the DirectionsService, the DirectionsRenderer and an eventListener for the GO button
//If I chose to implement a detailed steps display it would also be created here
const directionsService = new google.maps.DirectionsService();
const mapOptions = {
center: mapCenter,
zoom: 7,
}
map = new google.maps.Map(document.getElementById("map"), mapOptions);
const directionsRenderer = new google.maps.DirectionsRenderer({map: map});
//const stepDisplay = new google.maps.InfoWindow();
const geocoder = new google.maps.Geocoder();
document.getElementById("submit").addEventListener("click", () => {
launcher(geocoder, directionsRenderer, directionsService);
});
}
async function launcher(geocoder, directionsRenderer, directionsService){
//the method is used to be launched by the eventListener
//it sets up the start and end points, fetches the EV markers and launches the route calculation process though a callback function
resetMarkers();
const startEndPointsArray = await setupRoutingProcess(geocoder);
await callbackHandler(startEndPointsArray,directionsRenderer,
directionsService, calculateAndDisplayRoute);
}
function setMapOnAll(map){
// Sets the map on all markers in the array.
for (let i = 0; i < markerArray.length; i++) {
markerArray[i].setMap(map);
}
}
function clearMarkers() {
// Removes the markers from the map, but keeps them in the array.
setMapOnAll(null);
}
function resetMarkers(){
// Pushes all visible markers to a same array,
// launches the different reset processes and
// deletes all markers in the arrays by removing references to them.
for (let i = 0; i < chargingPointsMarkers.length; i++) {
markerArray.push(chargingPointsMarkers[i])
}
chargingPointsMarkers = [];
for (let j = 0; j < stopoverMarkers.length; j++) {
markerArray.push(stopoverMarkers[j])
}
stopoverMarkers = [];
clearMarkers();
markerArray = []
}
async function setupRoutingProcess(geocoder){
//launches the setGeocodeAddress method for both start and end points and stores them in an array
start = await setGeocodeAddress(geocoder, map, "start");
end = await setGeocodeAddress(geocoder, map, "end");
let startEndPointsArray = [start];
startEndPointsArray.push(end);
return startEndPointsArray;
}
async function setGeocodeAddress(geocoder, resultsMap, elementId) {
//Retrieve the addresses (strings) from the html text boxes and uses Geocoder to Google Markers objects.
//it pushes those markers in an array later used to delete the markers on the map
const address = document.getElementById(elementId).value;
return new Promise(resolve => geocoder.geocode({address: address},
(results, status) => {
if (status === "OK") {
resultsMap.setCenter(results[0].geometry.location);
const marker = new google.maps.Marker({
map: resultsMap,
position: results[0].geometry.location,
title: elementId,
});
resolve(marker)
markerArray.push(marker);
} else {
alert("Trip Route finder was not successful for the following reason: " + status);
}
}));
}
async function callbackHandler (startEndPointsArray,
directionsRenderer,
directionsService,
calculateAndDisplayRoute){
//
let jsonChargingPoints = await setChargingStationsMarkers(startEndPointsArray, directionsRenderer,
directionsService, calculateAndDisplayRoute);
await createChargerPointMarkers(jsonChargingPoints)
calculateAndDisplayRoute(
directionsRenderer,
directionsService,
jsonChargingPoints
);
}
async function setChargingStationsMarkers(startEndPointsArray, directionsRenderer,
directionsService, calculateAndDisplayRoute) {
//Creates an encoded polyline to be passed as an Url argument to limit the results
//fetches the EV Charging Points as Json response
const polyline = await createPolyline(startEndPointsArray);
const baseUrl = 'https://api.openchargemap.io/v3/poi/?output=json&maxresults=200&includecomments=true';
const queryUrl = baseUrl + '&polyline=' + polyline + '&distance=50';
let data = await fetch(queryUrl)
.then((response) => response.json())
.then((data) => {return data})
return data;
}
async function createPolyline(startEndPointsArray){
//Creates a polyline and encodes it
try {
position = startEndPointsArray[0].getPosition();
position2 = startEndPointsArray[1].getPosition();
const initialPath = [position, position2];
const poly = new google.maps.Polyline({
path: initialPath,
strokeColor: '#ff0000',
strokeOpacity: 0.00001,
strokeWeight: 0,
});
const path = poly.getPath();
const encodedPath = await google.maps.geometry.encoding.encodePath(path);
return encodedPath;
}catch (error){
throw error ('Failed to create polyline');
}
}
function createChargerPointMarkers(jsonChargingPoints) {
//Loop through the Json response and launch the PlaceMarkers function
for (let x = 0; x < jsonChargingPoints.length; x++) {
const LatLng = new google.maps.LatLng(parseFloat(jsonChargingPoints[x].AddressInfo.Latitude), parseFloat(jsonChargingPoints[x].AddressInfo.Longitude));
placeMarker(LatLng);
}
}
function placeMarker(location) {
//Convert the Json response elements to Google Markers, places them on the Map and pushes them to an array.
let marker = new google.maps.Marker({
position: location,
map,
draggable: false,
});
chargingPointsMarkers.push(marker)
}
async function calculateAndDisplayRoute(
directionsRenderer,
directionsService,
jsonChargingPoints,
stepDisplay,
map) {
if (!compareVehicleCapacityToDistance(vehicle1, start)) {
setChargeCheckpoint(vehicle1)
}
directionsService.route(setRequest(),
function (result, status) {
if (status === "OK") {
directionsRenderer.setDirections(result);
// showSteps(result, markerArray, stepDisplay, map);
} else {
window.alert("Directions request failed due to " + status);
}
});
}
function setRequest(){
//prepares the request sent to the Directions service
let stopovers = [];
for (let x = 0; x < stopoverMarkers.length; x++){
let latLng = stopoverMarkers[x].getPosition();
let waypoint = {
location: latLng,
stopover: true
};
stopovers.push(waypoint)
}
const request = {
origin: start.getPosition(),
destination: end.getPosition(),
waypoints: stopovers,
travelMode: google.maps.TravelMode.DRIVING,
unitSystem: google.maps.UnitSystem.METRIC
};
return request;
}
function compareVehicleCapacityToDistance(vehicle, p1){
//Checks if the distance to destination is greater than the vehicle capacity
if (calculateDistance(p1, end) > (vehicle.status*vehicle.consumption)){
return false
}return true;
}
function setChargeCheckpoint(vehicle){
//launches the function selecting the closest marker to destination
//Setting a marker of the selected marker on the map (might be redundant)
//Pushes it to markerArray for later deletion (might be redundant)
//Pushes it to stopoverMarkers to be used by the Directions service to setup a route
let waypoint = selectMarkerClosestToDestination(vehicle);
const waypointLocation = waypoint.getPosition();
const marker = new google.maps.Marker({
position: waypointLocation,
stopover: true,
draggable: false,
title: "EV charging stopover"
});
markerArray.push(marker)
stopoverMarkers.push(marker)
}
function selectMarkerClosestToDestination(vehicle) {
//Selecting the closest marker to destination as long as it is not out of the vehicle capacity range
//CURRENTLY BUGGED
let waypoints = chargingPointsMarkers;
for (let x = waypoints.length -1; x > 0; x--) {
if(calculateDistance(waypoints[x], start) > (vehicle.status*vehicle.consumption)){
console.log(calculateDistance(waypoints[x], start))
console.log(vehicle.status*vehicle.consumption)
waypoints.splice(x, 1)
console.log(waypoints)
}
}
console.log(waypoints)
for (let x = waypoints.length - 1; x > 0; x--) {
if (calculateDistance(waypoints[x], end) > (calculateDistance(waypoints[x-1], end))) {
waypoints.splice(x, 1);
} else {
waypoints.splice(x - 1, 1);
}
}
console.log(waypoints)
return waypoints[0];
}
function calculateDistance(p1, p2) {
//Uses the Google geometry library to calculate distance between two Markers
let a = p1.getPosition();
let b = p2.getPosition();
let distance = (google.maps.geometry.spherical.computeDistanceBetween(a, b) / 1000).toFixed(2);
return distance;
}
function showSteps(directionResult, stepDisplay, map) {
// For each step, place a marker, and add the text to the marker's infowindow.
// Also attach the marker to an array so we can keep track of it and remove it
// when calculating new routes.
//NOT CURRENTLY IMPLEMENTED/USED
const myRoute = directionResult.routes[0].legs[0];
for (let i = 0; i < myRoute.steps.length; i++) {
const marker = (markerArray[i] =
markerArray[i] || new google.maps.Marker());
marker.setMap(map);
marker.setPosition(myRoute.steps[i].start_location);
attachInstructionText(
stepDisplay,
marker,
myRoute.steps[i].instructions,
map
);
}
}
function attachInstructionText(stepDisplay, marker, text, map) {
google.maps.event.addListener(marker, "click", () => {
// Open an info window when the marker is clicked on, containing the text
// of the step.
//NOT CURRENTLY IMPLEMENTED/USED
stepDisplay.setContent(text);
stepDisplay.open(map, marker);
});
}
/* Always set the map height explicitly to define the size of the div
* element that contains the map. */
#map {
height: 100%;
}
/* Optional: Makes the sample page fill the window. */
html,
body {
height: 100%;
margin: 0;
padding: 0;
}
#floating-panel {
position: absolute;
top: 10px;
left: 25%;
z-index: 5;
background-color: #fff;
padding: 5px;
border: 1px solid #999;
text-align: center;
font-family: "Roboto", "sans-serif";
line-height: 30px;
padding-left: 10px;
}
#warnings-panel {
width: 100%;
height: 10%;
text-align: center;
}
<!DOCTYPE html>
<html>
<head>
<title>EV Trip Route Finder</title>
<script src="https://polyfill.io/v3/polyfill.min.js?features=default"></script>
<script
src="https://maps.googleapis.com/maps/api/js?key=AIzaSyCkUOdZ5y7hMm0yrcCQoCvLwzdM6M8s5qk&callback=initMap&libraries=&v=weekly"
defer
></script>
<!-- jsFiddle will insert css and js -->
</head>
<body>
<div id="floating-panel" >
<b>Start: </b>
<input id="start" type="text" value="Tallinn">
<b>End: </b>
<input id="end" type="text" value="Vilnius">
<input id="submit" type="button" value="GO" />
</div>
<div id="map"></div>
<div id="warnings-panel"></div>
</body>
</html>
There is only one small mistake in your code, that is in your for loop where you exclude waypoints that are over the vehicle capacity.
Basically, you are doing the following, which as you can see doesn't output what you'd expect:
const a = [1, 2, 3, 4, 5];
for (let i = a.length - 1; i > 0; i--) {
console.log(a[i])
}
Instead, you want to use i >= 0 to loop through all elements within your array:
const a = [1, 2, 3, 4, 5];
for (let i = a.length - 1; i >= 0; i--) {
console.log(a[i])
}
Changing this one statement seems to correct the issue because you were not iterating through the entire array and therefore you were leaving 1 element at 528km from start in the first loop, which would end up being the one you select in the second loop (in the selectMarkerClosestToDestination() function).
let map;
let mapCenter = { lat: 59.428, lng: 24.76};
let start;
let end;
let chargingPointsMarkers = [];
let markerArray = [];
let stopoverMarkers = []
let vehicle1 = {capacity: 33, status: 33, consumption: 6.6666} //1KW = 6.6666 Km; Capacity in KM = status*consumption;
function initMap(listener) {
//Create the map, the DirectionsService, the DirectionsRenderer and an eventListener for the GO button
//If I chose to implement a detailed steps display it would also be created here
const directionsService = new google.maps.DirectionsService();
const mapOptions = {
center: mapCenter,
zoom: 7,
}
map = new google.maps.Map(document.getElementById("map"), mapOptions);
const directionsRenderer = new google.maps.DirectionsRenderer({map: map});
//const stepDisplay = new google.maps.InfoWindow();
const geocoder = new google.maps.Geocoder();
document.getElementById("submit").addEventListener("click", () => {
launcher(geocoder, directionsRenderer, directionsService);
});
}
async function launcher(geocoder, directionsRenderer, directionsService){
//the method is used to be launched by the eventListener
//it sets up the start and end points, fetches the EV markers and launches the route calculation process though a callback function
resetMarkers();
const startEndPointsArray = await setupRoutingProcess(geocoder);
await callbackHandler(startEndPointsArray,directionsRenderer,
directionsService, calculateAndDisplayRoute);
}
function setMapOnAll(map){
// Sets the map on all markers in the array.
for (let i = 0; i < markerArray.length; i++) {
markerArray[i].setMap(map);
}
}
function clearMarkers() {
// Removes the markers from the map, but keeps them in the array.
setMapOnAll(null);
}
function resetMarkers(){
// Pushes all visible markers to a same array,
// launches the different reset processes and
// deletes all markers in the arrays by removing references to them.
for (let i = 0; i < chargingPointsMarkers.length; i++) {
markerArray.push(chargingPointsMarkers[i])
}
chargingPointsMarkers = [];
for (let j = 0; j < stopoverMarkers.length; j++) {
markerArray.push(stopoverMarkers[j])
}
stopoverMarkers = [];
clearMarkers();
markerArray = []
}
async function setupRoutingProcess(geocoder){
//launches the setGeocodeAddress method for both start and end points and stores them in an array
start = await setGeocodeAddress(geocoder, map, "start");
end = await setGeocodeAddress(geocoder, map, "end");
let startEndPointsArray = [start];
startEndPointsArray.push(end);
return startEndPointsArray;
}
async function setGeocodeAddress(geocoder, resultsMap, elementId) {
//Retrieve the addresses (strings) from the html text boxes and uses Geocoder to Google Markers objects.
//it pushes those markers in an array later used to delete the markers on the map
const address = document.getElementById(elementId).value;
return new Promise(resolve => geocoder.geocode({address: address},
(results, status) => {
if (status === "OK") {
resultsMap.setCenter(results[0].geometry.location);
const marker = new google.maps.Marker({
map: resultsMap,
position: results[0].geometry.location,
title: elementId,
});
resolve(marker)
markerArray.push(marker);
} else {
alert("Trip Route finder was not successful for the following reason: " + status);
}
}));
}
async function callbackHandler (startEndPointsArray,
directionsRenderer,
directionsService,
calculateAndDisplayRoute){
//
let jsonChargingPoints = await setChargingStationsMarkers(startEndPointsArray, directionsRenderer,
directionsService, calculateAndDisplayRoute);
await createChargerPointMarkers(jsonChargingPoints)
calculateAndDisplayRoute(
directionsRenderer,
directionsService,
jsonChargingPoints
);
}
async function setChargingStationsMarkers(startEndPointsArray, directionsRenderer,
directionsService, calculateAndDisplayRoute) {
//Creates an encoded polyline to be passed as an Url argument to limit the results
//fetches the EV Charging Points as Json response
const polyline = await createPolyline(startEndPointsArray);
const baseUrl = 'https://api.openchargemap.io/v3/poi/?output=json&maxresults=200&includecomments=true';
const queryUrl = baseUrl + '&polyline=' + polyline + '&distance=50';
let data = await fetch(queryUrl)
.then((response) => response.json())
.then((data) => {return data})
return data;
}
async function createPolyline(startEndPointsArray){
//Creates a polyline and encodes it
try {
position = startEndPointsArray[0].getPosition();
position2 = startEndPointsArray[1].getPosition();
const initialPath = [position, position2];
const poly = new google.maps.Polyline({
path: initialPath,
strokeColor: '#ff0000',
strokeOpacity: 0.00001,
strokeWeight: 0,
});
const path = poly.getPath();
const encodedPath = await google.maps.geometry.encoding.encodePath(path);
return encodedPath;
}catch (error){
throw error ('Failed to create polyline');
}
}
function createChargerPointMarkers(jsonChargingPoints) {
//Loop through the Json response and launch the PlaceMarkers function
for (let x = 0; x < jsonChargingPoints.length; x++) {
const LatLng = new google.maps.LatLng(parseFloat(jsonChargingPoints[x].AddressInfo.Latitude), parseFloat(jsonChargingPoints[x].AddressInfo.Longitude));
placeMarker(LatLng);
}
}
function placeMarker(location) {
//Convert the Json response elements to Google Markers, places them on the Map and pushes them to an array.
let marker = new google.maps.Marker({
position: location,
map,
draggable: false,
});
chargingPointsMarkers.push(marker)
}
async function calculateAndDisplayRoute(
directionsRenderer,
directionsService,
jsonChargingPoints,
stepDisplay,
map) {
if (!compareVehicleCapacityToDistance(vehicle1, start)) {
setChargeCheckpoint(vehicle1)
}
directionsService.route(setRequest(),
function (result, status) {
if (status === "OK") {
directionsRenderer.setDirections(result);
// showSteps(result, markerArray, stepDisplay, map);
} else {
window.alert("Directions request failed due to " + status);
}
});
}
function setRequest(){
//prepares the request sent to the Directions service
let stopovers = [];
for (let x = 0; x < stopoverMarkers.length; x++){
let latLng = stopoverMarkers[x].getPosition();
let waypoint = {
location: latLng,
stopover: true
};
stopovers.push(waypoint)
}
const request = {
origin: start.getPosition(),
destination: end.getPosition(),
waypoints: stopovers,
travelMode: google.maps.TravelMode.DRIVING,
unitSystem: google.maps.UnitSystem.METRIC
};
return request;
}
function compareVehicleCapacityToDistance(vehicle, p1){
//Checks if the distance to destination is greater than the vehicle capacity
if (calculateDistance(p1, end) > (vehicle.status*vehicle.consumption)){
return false
}return true;
}
function setChargeCheckpoint(vehicle){
//launches the function selecting the closest marker to destination
//Setting a marker of the selected marker on the map (might be redundant)
//Pushes it to markerArray for later deletion (might be redundant)
//Pushes it to stopoverMarkers to be used by the Directions service to setup a route
let waypoint = selectMarkerClosestToDestination(vehicle);
const waypointLocation = waypoint.getPosition();
const marker = new google.maps.Marker({
position: waypointLocation,
stopover: true,
draggable: false,
title: "EV charging stopover"
});
markerArray.push(marker)
stopoverMarkers.push(marker)
}
function selectMarkerClosestToDestination(vehicle) {
//Selecting the closest marker to destination as long as it is not out of the vehicle capacity range
//CURRENTLY BUGGED
let waypoints = chargingPointsMarkers;
for (let x = waypoints.length -1; x >= 0; x--) {
if(calculateDistance(waypoints[x], start) > (vehicle.status*vehicle.consumption)){
console.log(calculateDistance(waypoints[x], start))
console.log(vehicle.status*vehicle.consumption)
waypoints.splice(x, 1)
console.log(waypoints)
}
}
console.log(waypoints)
for (let x = waypoints.length - 1; x > 0; x--) {
if (calculateDistance(waypoints[x], end) > (calculateDistance(waypoints[x-1], end))) {
waypoints.splice(x, 1);
} else {
waypoints.splice(x - 1, 1);
}
}
console.log(waypoints)
return waypoints[0];
}
function calculateDistance(p1, p2) {
//Uses the Google geometry library to calculate distance between two Markers
let a = p1.getPosition();
let b = p2.getPosition();
let distance = (google.maps.geometry.spherical.computeDistanceBetween(a, b) / 1000).toFixed(2);
return distance;
}
function showSteps(directionResult, stepDisplay, map) {
// For each step, place a marker, and add the text to the marker's infowindow.
// Also attach the marker to an array so we can keep track of it and remove it
// when calculating new routes.
//NOT CURRENTLY IMPLEMENTED/USED
const myRoute = directionResult.routes[0].legs[0];
for (let i = 0; i < myRoute.steps.length; i++) {
const marker = (markerArray[i] =
markerArray[i] || new google.maps.Marker());
marker.setMap(map);
marker.setPosition(myRoute.steps[i].start_location);
attachInstructionText(
stepDisplay,
marker,
myRoute.steps[i].instructions,
map
);
}
}
function attachInstructionText(stepDisplay, marker, text, map) {
google.maps.event.addListener(marker, "click", () => {
// Open an info window when the marker is clicked on, containing the text
// of the step.
//NOT CURRENTLY IMPLEMENTED/USED
stepDisplay.setContent(text);
stepDisplay.open(map, marker);
});
}
/* Always set the map height explicitly to define the size of the div
* element that contains the map. */
#map {
height: 100%;
}
/* Optional: Makes the sample page fill the window. */
html,
body {
height: 100%;
margin: 0;
padding: 0;
}
#floating-panel {
position: absolute;
top: 10px;
left: 25%;
z-index: 5;
background-color: #fff;
padding: 5px;
border: 1px solid #999;
text-align: center;
font-family: "Roboto", "sans-serif";
line-height: 30px;
padding-left: 10px;
}
#warnings-panel {
width: 100%;
height: 10%;
text-align: center;
}
<!DOCTYPE html>
<html>
<head>
<title>EV Trip Route Finder</title>
<script src="https://polyfill.io/v3/polyfill.min.js?features=default"></script>
<script
src="https://maps.googleapis.com/maps/api/js?key=AIzaSyCkUOdZ5y7hMm0yrcCQoCvLwzdM6M8s5qk&callback=initMap&libraries=&v=weekly"
defer
></script>
<!-- jsFiddle will insert css and js -->
</head>
<body>
<div id="floating-panel" >
<b>Start: </b>
<input id="start" type="text" value="Tallinn">
<b>End: </b>
<input id="end" type="text" value="Vilnius">
<input id="submit" type="button" value="GO" />
</div>
<div id="map"></div>
<div id="warnings-panel"></div>
</body>
</html>
I am using the Bing Maps v8 API. I have a map that shows a driving route, and a pushpin of another location. I need to get the map to zoom out to show both the waypoint locations and the pushpin location. Right now, I can get the map to zoom to the waypoints of the pushpin location.
I am not sure how to get both to show on the map zoom. I know that I need to use the LocationRect class.
var searchManager;
var startingPoint = document.getElementById('startPoint').value;
var address = $("#addressLine").val();
var loc;
var patAddLoc;
var waypointLoc;
var pinsLocs = [];
// GET ROUTE BASED ON DIRECTION
function GetMap() {
var map = new Microsoft.Maps.Map(document.getElementById('myMap'), {
//BING API KEY VIA AZURE
credentials: 'X',
zoom: 10,
});
// geocode patient address
geocodeQuery(address);
// look for patient address
function geocodeQuery(query) {
//If search manager is not defined, load the search module.
if (!searchManager) {
//Create an instance of the search manager and call the geocodeQuery function again.
Microsoft.Maps.loadModule('Microsoft.Maps.Search', function () {
searchManager = new Microsoft.Maps.Search.SearchManager(map);
geocodeQuery(query);
});
} else {
var searchRequest = {
where: query,
callback: function (r) {
//Add the first result to the map and zoom into it.
if (r && r.results && r.results.length > 0) {
var pin = new Microsoft.Maps.Pushpin(r.results[0].location);
// get patient address location
patAddLoc = r.results[0].location;
map.entities.push(pin);
// add patient location to pushpin array
pinsLocs.push(patAddLoc);
// view is set with route location
//map.setView({ bounds: r.results[0].bestView });
}
// directions manager
// addWaypoint
Microsoft.Maps.loadModule('Microsoft.Maps.Directions', function addWaypoint() {
var directionsManager = new Microsoft.Maps.Directions.DirectionsManager(map);
// if less than 2 point on the map
if (directionsManager.getAllWaypoints().length < 2) {
directionsManager.clearAll();
var startWaypoint = new Microsoft.Maps.Directions.Waypoint({ address: document.getElementById('startPoint').value });
directionsManager.addWaypoint(startWaypoint);
var endWaypoint = new Microsoft.Maps.Directions.Waypoint({ address: document.getElementById('endPoint').value });
directionsManager.addWaypoint(endWaypoint);
}
// Insert a waypoint
if (document.getElementById('wayPoints').value !== null) {
var addressList = JSON.parse(document.getElementById('wayPoints').value);
var arrayLength = addressList.length;
// insert waypoints from schedule
for (var i = 0; i < arrayLength; i++) {
//alert(addressList[i]);
var hvaddress = addressList[i];
var newWP = new Microsoft.Maps.Directions.Waypoint({ address: hvaddress });
directionsManager.addWaypoint(newWP, 1);
}
}
// Set the element in which the itinerary will be rendered - set autoupdatemapview to false to override autozoom to route
directionsManager.setRenderOptions({ itineraryContainer: document.getElementById('printoutPanel'), autoUpdateMapView: false });
directionsManager.calculateDirections();
var allWaypoints = directionsManager.getAllWaypoints();
// add way point to pinsLocs array
for (var i = 0; i < allWaypoints.length; i++) {
// returns nulls
alert(allWaypoints);
loc = allWaypoints[i].getLocation();
var showMeLoc = loc;
// showMeLoc = undefined.....?
alert(showMeLoc);
pinsLocs.push(loc);
}
// only the address search location is added to the array, waypoint locations are null
alert(pinsLocs);
//Create a LocationRect from array of locations and set the map view.
map.setView({
bounds: Microsoft.Maps.LocationRect.fromLocations(pinsLocs),
padding: 100 //Add a padding to buffer map to account for pushpin pixel dimensions
});
});
},
errorCallback: function (e) {
//If there is an error, alert the user about it.
alert("No results found.");
}
};
//Make the geocode request.
searchManager.geocode(searchRequest);
}
}
}
UPDATE - HERE is the working code for those who have the same question!
var searchManager;
var startingPoint = document.getElementById('startPoint').value;
var address = $("#addressLine").val();
var patAddLoc;
// GET ROUTE BASED ON DIRECTION
function GetMap() {
var map = new Microsoft.Maps.Map(document.getElementById('myMap'), {
//BING API KEY VIA AZURE
credentials: 'XXXXX',
zoom: 10,
});
// geocode patient address
geocodeQuery(address);
// look for patient address
function geocodeQuery(query) {
//If search manager is not defined, load the search module.
if (!searchManager) {
//Create an instance of the search manager and call the geocodeQuery function again.
Microsoft.Maps.loadModule('Microsoft.Maps.Search', function () {
searchManager = new Microsoft.Maps.Search.SearchManager(map);
geocodeQuery(query);
});
} else {
var searchRequest = {
where: query,
callback: function (r) {
//Add the first result to the map and zoom into it.
if (r && r.results && r.results.length > 0) {
locs = [];
var pin = new Microsoft.Maps.Pushpin(r.results[0].location);
// get patient address location
patAddLoc = r.results[0].location;
map.entities.push(pin);
// add patient location to pushpin array
locs.push(r.results[0].location);
// view is set with route location
//map.setView({ bounds: r.results[0].bestView });
}
// directions manager
// addWaypoint
Microsoft.Maps.loadModule('Microsoft.Maps.Directions', function addWaypoint() {
var directionsManager = new Microsoft.Maps.Directions.DirectionsManager(map);
// if less than 2 point on the map
if (directionsManager.getAllWaypoints().length < 2) {
directionsManager.clearAll();
var startWaypoint = new Microsoft.Maps.Directions.Waypoint({ address: document.getElementById('startPoint').value });
directionsManager.addWaypoint(startWaypoint);
var endWaypoint = new Microsoft.Maps.Directions.Waypoint({ address: document.getElementById('endPoint').value });
directionsManager.addWaypoint(endWaypoint);
}
// Insert a waypoint
if (document.getElementById('wayPoints').value !== null) {
var addressList = JSON.parse(document.getElementById('wayPoints').value);
var arrayLength = addressList.length;
// insert waypoints from schedule
for (var i = 0; i < arrayLength; i++) {
//alert(addressList[i]);
var hvaddress = addressList[i];
var newWP = new Microsoft.Maps.Directions.Waypoint({ address: hvaddress });
directionsManager.addWaypoint(newWP, 1);
}
}
// Set the element in which the itinerary will be rendered - set autoupdatemapview to false to override autozoom to route
directionsManager.setRenderOptions({ itineraryContainer: document.getElementById('printoutPanel'), autoUpdateMapView: false });
//Add event handlers to directions manager.
Microsoft.Maps.Events.addHandler(directionsManager, 'directionsError', directionsError);
Microsoft.Maps.Events.addHandler(directionsManager, 'directionsUpdated', directionsUpdated);
// Calculate directions, which displays a route on the map
directionsManager.calculateDirections();
// get route boundaries
function directionsUpdated(e) {
//Get the current route index.
var routeIdx = directionsManager.getRequestOptions().routeIndex;
// get northeast bounding box corner
var bBoxNE = e.routeSummary[routeIdx].northEast;
locs.push(bBoxNE);
// get southwest bounding box corner
var bBoxSW = e.routeSummary[routeIdx].southWest;
locs.push(bBoxSW);
//SET MAP VIEW
//Create a LocationRect from array of locations and set the map view.
map.setView({
bounds: Microsoft.Maps.LocationRect.fromLocations(locs),
padding: 50 //Add a padding to buffer map to account for pushpin pixel dimensions
});
}
function directionsError(e) {
alert('Error: ' + e.message + '\r\nResponse Code: ' + e.responseCode)
}
});
},
errorCallback: function (e) {
//If there is an error, alert the user about it.
alert("No results found.");
}
};
//Make the geocode request.
searchManager.geocode(searchRequest);
}
}
}
If I understand correctly, you want a view that the entirety of the route and the extra location are visible?
How about using directionsManager.getCurrentRoute().routePath to get all the locations on the route, and then using LocationRect.fromLocations(), which can take in an array of locations - in your case all locations of above plus the additional one.
Note that calculateDirections and geocode and asynchronous, so you may want to handle the dependency on patAddLoc (e.g. move the directions code into the geocode callback).
Reference:
https://msdn.microsoft.com/en-us/library/mt750375.aspx
https://msdn.microsoft.com/en-us/library/mt750645.aspx
https://msdn.microsoft.com/en-us/library/mt712644.aspx
I can't create multiple layers (images and lines) on the same coordinates.
Does anyone know how you can handle it?
example code:
for (; index_array < array_trip.length; index_array++) {
latVal = array_trip[index_array].latitude;
longVal = Microsoft.Maps.Location.normalizeLongitude(array_trip[index_array].longitude);
map.setView({ center: new Microsoft.Maps.Location(latVal, longVal) });
var pushpinOptions = { icon: path + 'car.png', width: 50, height: 50 };
var pushpin = new Microsoft.Maps.Pushpin({ latitude: latVal, longitude: longVal }, pushpinOptions);
map.entities.push(pushpin);
}
First off, don't set the map view in an array. This will only cause issues. Secondly, ensure that the URL to the pushpin icon is correct. Perhaps try removing that option for now until you see the default pushpins displayed on the map, then try adding a custom icon.
If you want to separate your data into layers you should use EntityCollection's: https://msdn.microsoft.com/en-us/library/gg427616.aspx
Here is a good blog post on layering: https://rbrundritt.wordpress.com/2011/10/13/multiple-pushpins-and-infoboxes-in-bing-maps-v7/
Use could initialize EntityCollection object to add multiple entities to the map at one time.
Example
function GetMap() {
var locations = [
new Microsoft.Maps.Location(60.173783, 24.941068),
new Microsoft.Maps.Location(59.338575, 18.065823),
new Microsoft.Maps.Location(59.922602, 10.749411),
new Microsoft.Maps.Location(55.675817, 12.570452)
];
var map = new Microsoft.Maps.Map(document.getElementById("mapDiv"), { credentials: "Bing Maps Key" });
//1. Add pushpins
for (var i = 0; i < locations.length; i++) {
var pin = new Microsoft.Maps.Pushpin(locations[i]);
// Add the pushpin
map.entities.push(pin);
}
//2. Add a polyline
var line = new Microsoft.Maps.Polyline(locations);
map.entities.push(line);
//3. Add a polygon
var polygoncolor = new Microsoft.Maps.Color(100, 100, 0, 100);
var polygon = new Microsoft.Maps.Polygon(locations, { fillColor: polygoncolor, strokeColor: polygoncolor });
map.entities.push(polygon);
var bestview = Microsoft.Maps.LocationRect.fromLocations(locations);
map.setView({ bounds: bestview });
}
<script type="text/javascript" src="http://ecn.dev.virtualearth.net/mapcontrol/mapcontrol.ashx?v=7.0"></script>
<body onload="GetMap();">
<div id='mapDiv' style="position:relative; width:600px; height:600px;"></div>
</body>
I have to manage a map of about 80.000 markers concentrated in France.
To do that, I decided to get the bounds of the viewport and call a dynamic-JSON (with PHP) which contains the markers inside the viewport. And this on the "idle" event.
I faced a problem with this solution. Indeed, the markers which already exist was re-plotted (at the same position), which consequently weigh the map for nothing...
To solve it, the markers list before and after the JSON query are compared (thanks to jQuery), in order to plot only the new markers. And it works!
Now, I would want to remove the markers which are not currently shown on the map. Or a list of markers (I get it thanks to jQuery) designated by an ID which is also the title of the marker. So, how can a delete markers like that ? I specify that I am using MarkerManager.
Otherwise, you guess that if I do not remove these markers, they will be re-plotted in some cases... For example, you are viewing the city A, you move the map to view the city B, and you get back to the city A...
Here is the code:
var map;
var mgr;
var markers = [];
function initialize(){
var mapOptions = {
zoom: 6,
center: new google.maps.LatLng(46.679594, 2.109375)
};
map = new google.maps.Map(document.getElementById('map_canvas'), mapOptions);
var mgrOptions = { borderPadding: 50, maxZoom: 15, trackMarkers: false };
mgr = new MarkerManager(map, mgrOptions);
google.maps.event.addListener(map, 'idle', function() {
mapEvent();
});
}
function mapEvent(){
if( map.getZoom() >= 8 ){
var bounds = map.getBounds();
getSupports(bounds.getNorthEast(), bounds.getSouthWest());
} else {
// Todo
}
}
var markerslistID = new Array();
var markerslistData = {};
function getSupports(ne, sw){
newMarkerslistID = new Array();
newMarkerslistData = {};
// Getting the markers of the current view
$.getJSON('./markerslist.php?nelat='+ne.lat()+'&nelng='+ne.lng()+'&swlat='+sw.lat()+'&swlng='+sw.lng(), function(data) {
for (var i = 0; i < data.points.length; i++) {
var val = data.points[i];
newMarkerslistID.push(val.id);
newMarkerslistData[val.id] = new Array(val.lat, val.lng, val.icon);
}
// List of New Markers TO PLOT
var diffNewMarkers = $(newMarkerslistID).not(markerslistID).get();
// List of Old markers TO REMOVE
var diffOldMarkers = $(markerslistID).not(newMarkerslistID).get();
// Plotting the NEW MARKERS
for( var i = 0; i < diffNewMarkers.length; i++ ){
var marker = new google.maps.Marker({
position: new google.maps.LatLng(newMarkerslistData[diffNewMarkers[i]][0], newMarkerslistData[diffNewMarkers[i]][1]),
title : diffNewMarkers[i],
icon : './images/'+newMarkerslistData[diffNewMarkers[i]][2]+'.png'
});
mgr.addMarker(marker, 0);
}
/*****************************************
HERE WE HAVE TO REMOVE
THE MARKERS CONTAINED IN diffOldMarkers
*****************************************/
mgr.refresh();
// Switching the new list to the old, for the next event
markerslistID = newMarkerslistID;
markerslistData = newMarkerslistData;
});
}
Thank you for your help.
A one-liner to hide all markers that ar not in the current viewport.
!map.getBounds().contains(marker.getPosition()) && marker.setVisible(false);
Or,
if (map.getBounds().contains(marker.getPosition()) && !marker.getVisible()) {
marker.setVisible(true);
}
else if (!map.getBounds().contains(marker.getPosition()) && marker.getVisible()) {
marker.setVisible(false);
}