Google 标记数组错误 - 选择了错误的标记元素
Google Markers array bug - selecting the wrong marker element
我制作了一个 API 用于在地图上获取电动汽车充电点并创建路线。
如果车辆容量不足以完成行程,路线应自动添加充电点。
我获取了一个 Json 并创建了一个 Marker 对象数组。这很好用。
然后我将它传递给一个循环,该循环应该将所有标记拼接得离起点太远而无法到达,然后另一个循环选择最接近目的地的标记。
出于某种我不明白的原因,虽然数组似乎已被正确修剪,但结果是一个标记就在目的地旁边,完全超出起点的范围。
我正在使用 Google 中的以下库:方向、地理编码、地图 Javascript...
我希望有人能帮助我,因为我完全被困在这里。
编辑(进一步解释:
该程序旨在在行程超过车辆容量(即 220 公里)时添加一个充电中途停留点。
所有符合条件的中途停留点都显示在地图上并推入一个数组(作为标记对象)。
该函数首先拼接所有超过 220 公里的中途停留点。从数组中,然后在另一个循环中,我选择了离目的地最近的中途停留地。
但是,如果您仔细观察路线,您会发现充电中途停留点(B 点)实际上是 550 公里。从出发和 2 公里。从到达。
这个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;
}
这是完整代码(代码片段):
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>
您的代码中只有一个小错误,即在您的 for
循环中您排除了 waypoints 超出车辆容量的内容。
基本上,您正在执行以下操作,如您所见,它不会输出您期望的内容:
const a = [1, 2, 3, 4, 5];
for (let i = a.length - 1; i > 0; i--) {
console.log(a[i])
}
相反,您想使用 i >= 0
遍历数组中的所有元素:
const a = [1, 2, 3, 4, 5];
for (let i = a.length - 1; i >= 0; i--) {
console.log(a[i])
}
更改这个语句似乎可以解决问题,因为您没有遍历整个数组,因此您在第一个循环中从开始处的 528 公里处留下了 1 个元素,最终将成为您 select 在第二个循环中(在 selectMarkerClosestToDestination()
函数中)。
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>
我制作了一个 API 用于在地图上获取电动汽车充电点并创建路线。
如果车辆容量不足以完成行程,路线应自动添加充电点。
我获取了一个 Json 并创建了一个 Marker 对象数组。这很好用。
然后我将它传递给一个循环,该循环应该将所有标记拼接得离起点太远而无法到达,然后另一个循环选择最接近目的地的标记。
出于某种我不明白的原因,虽然数组似乎已被正确修剪,但结果是一个标记就在目的地旁边,完全超出起点的范围。
我正在使用 Google 中的以下库:方向、地理编码、地图 Javascript...
我希望有人能帮助我,因为我完全被困在这里。
编辑(进一步解释:
该程序旨在在行程超过车辆容量(即 220 公里)时添加一个充电中途停留点。 所有符合条件的中途停留点都显示在地图上并推入一个数组(作为标记对象)。 该函数首先拼接所有超过 220 公里的中途停留点。从数组中,然后在另一个循环中,我选择了离目的地最近的中途停留地。 但是,如果您仔细观察路线,您会发现充电中途停留点(B 点)实际上是 550 公里。从出发和 2 公里。从到达。 这个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;
}
这是完整代码(代码片段):
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>
您的代码中只有一个小错误,即在您的 for
循环中您排除了 waypoints 超出车辆容量的内容。
基本上,您正在执行以下操作,如您所见,它不会输出您期望的内容:
const a = [1, 2, 3, 4, 5];
for (let i = a.length - 1; i > 0; i--) {
console.log(a[i])
}
相反,您想使用 i >= 0
遍历数组中的所有元素:
const a = [1, 2, 3, 4, 5];
for (let i = a.length - 1; i >= 0; i--) {
console.log(a[i])
}
更改这个语句似乎可以解决问题,因为您没有遍历整个数组,因此您在第一个循环中从开始处的 528 公里处留下了 1 个元素,最终将成为您 select 在第二个循环中(在 selectMarkerClosestToDestination()
函数中)。
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>