This question already has an answer here:
Midpoint of route in google maps
(1 answer)
Closed 6 years ago.
I've been playing around with the Google Maps/Google directions API. Does anyone have an inkling on how I might be able to find the mid-point along a route, not the geographic midpoint.
Ideally I'd like to find the lat and long values of this midpoint.
Any thoughts? I'm a bit stumped and hoping I may be able to find a suggestion without going crazy trying to find the answer myself.
You can use the GetPointAtDistance prototype from Gisgraphy. The prototype returns a LatLng for a specified distance along a polyline. The following code:
Define a polyline
Determine the half length of this polyline
Use the prototype to return the midpoint LatLng
Extract Lat and Lng from the midpoint LatLng
var polyline = new google.maps.Polyline({
path: [ new google.maps.LatLng(..., ...),
new google.maps.LatLng(..., ...),
... ];
}), //1.
midDistanceLength = polyline.getPath().getLength() / 2, //2.
midDistanceLatLng = polyline.GetPointAtDistance(midDistanceLength),//3.
midDistanceLat = midDistanceLatLng.lat(), //4.
midDistanceLng = midDistanceLatLng.lng(); //4.
//The prototype from Gisgraphy:
google.maps.Polygon.prototype.GetPointAtDistance = function(metres) {
// some awkward special cases
if (metres == 0) return this.getPath().getAt(0);
if (metres < 0) return null;
if (this.getPath().getLength() < 2) return null;
var dist=0;
var olddist=0;
for (var i=1; (i < this.getPath().getLength() && dist < metres); i++) {
olddist = dist;
dist += google.maps.geometry.spherical.computeDistanceBetween (
this.getPath().getAt(i),
this.getPath().getAt(i-1)
);
}
if (dist < metres) return null;
var p1= this.getPath().getAt(i-2);
var p2= this.getPath().getAt(i-1);
var m = (metres-olddist)/(dist-olddist);
return new google.maps.LatLng( p1.lat() + (p2.lat()-p1.lat())*m, p1.lng() + (p2.lng()-p1.lng())*m);
}
google.maps.Polyline.prototype.GetPointAtDistance = google.maps.Polygon.prototype.GetPointAtDistance;
An easiest way is that you can first:
1) Calculate total distance of the path using GMSGeometryDistance, by calculating the distance between every two subsequent points by GMSGeometryDistance function, then summing all the distance.
2) Then you calculate again, and summing in each step. When the sum is about half of the total distance, then you are at the middle point.
Sample code is as following:
func findTotalDistanceOfPath(path: GMSPath) -> Double {
let numberOfCoords = path.count()
var totalDistance = 0.0
if numberOfCoords > 1 {
var index = 0 as UInt
while index < numberOfCoords{
//1.1 cal the next distance
var currentCoord = path.coordinateAtIndex(index)
var nextCoord = path.coordinateAtIndex(index + 1)
var newDistance = GMSGeometryDistance(currentCoord, nextCoord)
totalDistance = totalDistance + newDistance
index = index + 1
}
}
return totalDistance
}
func findMiddlePointInPath(path: GMSPath ,totalDistance distance:Double) -> CLLocationCoordinate2D? {
let numberOfCoords = path.count()
let halfDistance = distance/2
let threadhold = 10 //10 meters
var midDistance = 0.0
if numberOfCoords > 1 {
var index = 0 as UInt
while index < numberOfCoords{
//1.1 cal the next distance
var currentCoord = path.coordinateAtIndex(index)
var nextCoord = path.coordinateAtIndex(index + 1)
var newDistance = GMSGeometryDistance(currentCoord, nextCoord)
midDistance = midDistance + newDistance
if fabs(midDistance - halfDistance) < threadhold { //Found the middle point in route
return nextCoord
}
index = index + 1
}
}
return nil //Return nil if we cannot find middle point in path for some reason
}
There is more to optimize the function. I wrote a detail answer in Swift in here
Related
I am running an object-oriented classification and I get gaps in the rasters that I want to use.
For example, I want to use elevation data from 'USGS/SRTMGL1_003'. If you run the code, you will see a gap in the elevation layer. I believe something happens when I use the command .reduceConnectedComponents, because the areas with gaps are present in the original images, in the clusters obtained as a result of the SNIC algorithm, and in SRTM. The same thing happens when I set the object size and reproject the rasters. The gaps are different in size, but still present. Since I haven't reprojected it in this code, they will change as you zoom in/out, but they persist even when I set a scale and export the files.
This happens to all the metrics that I want to use (after applying the .reduceConnectedComponents command), not only elevation. My images have a spatial resolution of 2.5 m.
The code is in this link or below:
var clip = ee.FeatureCollection('users/rizayeva/DS1011-1040_geometry');
var image2 = ee.Image('users/rizayeva/corona_2_5m/DS1011-1040DA_2_5m_JPEG_1band_EPSG32638').rename('ds10111040da');
var image3 = ee.Image('users/rizayeva/corona_2_5m/DS1011-1040DF_2_5m_JPEG_1band_EPSG32638').rename('ds10111040df');
var img = ee.Image.cat([image2, image3]).divide(255);
Map.addLayer(img, {bands: ["ds10111040df", "ds10111040df", "ds10111040da"], gamma: 0.8}, 'DS1011-1040');
Map.centerObject(ee.Geometry.Point(44.5567, 41.7856), 10);
var elevation = ee.Image('USGS/SRTMGL1_003').select('elevation').clip(clip);
Map.addLayer(elevation, {min: 1, max: 3445, gamma: [1.85], opacity: 1}, 'Elevation', false);
//Segmentation
var seeds = ee.Algorithms.Image.Segmentation.seedGrid(40);
// Run SNIC on the regular square grid.
var snic = ee.Algorithms.Image.Segmentation.SNIC({
image: img,
size: 32,
compactness: 0.1,
connectivity: 4,
neighborhoodSize:256,
seeds: seeds
}).select(['ds10111040da_mean', 'ds10111040df_mean', 'clusters'], ['ds10111040da', 'ds10111040df', 'clusters']);
var clusters = snic.select('clusters')//.reproject({crs: 'EPSG:4326', scale: 18});
Map.addLayer(clusters.randomVisualizer(), {}, 'clusters', false);
Map.addLayer(snic, {bands: ['ds10111040da', 'ds10111040df'], min:0, max:1, gamma: 0.8}, 'means', false);
var elevation_10111040 = elevation.addBands(clusters).reduceConnectedComponents(ee.Reducer.mean(), 'clusters', 256);
Map.addLayer(elevation_10111040, {min: 1, max: 3445, gamma: [1.85], opacity: 1}, 'elevation_10111040');
// Export.image.toDrive({
// image: elevation_10111040,
// description: "elevation_10111040",
// folder: "elevation_10111040",
// scale: 2.5,
// region: clip,
// maxPixels:1e13,
// });
I am passing map by reference in the canSum function where i am mutating its value and adding pairs but at the end when I iterate over the map I find the value of map has not been updated.
canSum function is a recursive function which takes a number (targetSum) and an array and finds if it is possible to form targetSum by any combinations of number in the array (numbers can be repeated).
#include<iostream>
#include<vector>
#include<map>
using namespace std;
bool canSum(int targetSum,vector<int> a,map<int, bool> &m){
if(!(m.find(targetSum) == m.end()))
return m[targetSum];
if (targetSum == 0)
return true;
if(targetSum<0)
return false;
for (int num : a)
{
if (canSum(targetSum - num, a,m)==true)
{
// m[targetSum] = true;
m.insert(pair<int, bool>(targetSum, true));
return m[targetSum];
}
}
m[targetSum] = false;
return m[targetSum];
}
int main(){
int targetSum, t;
vector<int> a;
map<int, bool> m;
m[0] = true;
cout << "enter target" << endl;
cin >> targetSum;
cout << "enter array, press esc to stop entering"<<endl;
while(cin>>t){
a.push_back(t);
}
for (int j = 0; j < a.size(); j++)
{
cout << a[j]<<" ";
}
cout << endl;
for (auto itr = m.begin(); itr != m.end(); ++itr) {
cout << '\t' << itr->first
<< '\t' << itr->second << '\n';
}
if(canSum(targetSum, a,m)){
cout << endl << "true" << endl;
}
else cout << endl << "false" << endl;
return 0;
}
Please help me. Thank you.
The for loop to print the map should be after the function call like.
if(canSum(targetSum, a,m)){
cout << endl << "true" << endl;
}
else cout << endl << "false" << endl;
for (auto itr = m.begin(); itr != m.end(); ++itr) {
cout << '\t' << itr->first
<< '\t' << itr->second << '\n';
}
Instead of
for (auto itr = m.begin(); itr != m.end(); ++itr) {
cout << '\t' << itr->first
<< '\t' << itr->second << '\n';
}
if(canSum(targetSum, a,m)){
cout << endl << "true" << endl;
}
else cout << endl << "false" << endl;
To see mutations in the map due to the function
I try like this:
sum: function(arr) {
const s = 0;
for(const i=0; i<arr.length; i++) {
s = s + arr[i];
}
return s;
}
and output that in table like this:
{{#each something}}
<td>{{sum this}}</td>
{{/each}}
but in this case nothing appears, any suggestions?
[this] array contains numbers
50 | 10 | 10 | 10 | 5 | 5 |
60 | 10 | 10 | 10 | 5 | 5 |
50 | 10 | 10 | 10 | 10 | 10|
so result should be
160 | 30 | 30 | 30 | 20 | 20 |
but in my method return zero values.
If I do as #Christophe Thiry show me:
sum: function(item) {
result = item[0]
for (i=0; i<result.length; i++) {
for (j=1; j<item.length; j++) {
result[i] = result[i]+item[j][i];
}
}
return result.toString();
}
and just:
<td>{{sum this}}</td>
I get TypeError: Cannot read property 'length' of undefined
Schema model:
const StateResultSchema = new Schema({
electoralUnit: {
type: Schema.Types.ObjectId,
ref: 'ElectoralUnit',
required: true,
unique: true
},
allVotes: {
type: Number,
required: true
},
validVotes: {
type: Number,
required: true
},
invalidVotes: {
type: Number,
required: true
},
partyVotes: {
type: [Number],
required: true
}
});
this is how it look like in MongoDB Compass"
_id:ObjectId("5ac4e01d46fa2b21280bd981")
electoralUnit:ObjectId("5ab906612f30fe23dc592591")
allVotes:100
validVotes:90
invalidVotes:10
partyVotes:[50,10,10,10,5,5]
__v:0
You don't need the each helper, you have to use the helper that you've created directly so {{sum something}} and also review your sum helper because you iterate only over one array.
Look at the below snippet to see how to achieve that.
$(document).ready(function () {
var context = { "something" : [
{ "_id":"5ac4e01d46fa2b21280bd981", "electoralUnit":"5ab906612f30fe23dc592591", "allVotes":100, "validVotes":90, "invalidVotes":10, "partyVotes":[50,10,10,10,5,5], "__v":0},
{ "_id":"5ac4e01d46fa2b21280bd982", "electoralUnit":"5ab906612f30fe23dc592592", "allVotes":100, "validVotes":90, "invalidVotes":10, "partyVotes":[50,10,10,10,5,5], "__v":0},
{ "_id":"5ac4e01d46fa2b21280bd983", "electoralUnit":"5ab906612f30fe23dc592593", "allVotes":100, "validVotes":90, "invalidVotes":10, "partyVotes":[60,10,10,10,5,5 ], "__v":0},
{ "_id":"5ac4e01d46fa2b21280bd984", "electoralUnit":"5ab906612f30fe23dc592594", "allVotes":100, "validVotes":90, "invalidVotes":10, "partyVotes":[50,10,10,10,10,10], "__v":0}
]
};
Handlebars.registerHelper('sum', function(item) {
result = item[0].partyVotes;
for (i=0; i<result.length; i++) {
for (j=1; j<item.length; j++) {
result[i] = result[i]+item[j].partyVotes[i];
}
}
return result.toString();
});
var source = $("#sourceTemplate").html();
var template = Handlebars.compile(source);
var html = template(context);
$("#resultPlaceholder").html(html);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/4.0.5/handlebars.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script id="sourceTemplate" type="text/x-handlebars-template">
{{sum something}}
</script>
<br/>
<div id="resultPlaceholder">
</div>
I am using Google Maps Distance Matrix and what I need is two different distances,
at the moment I have the following:-
var start = arrObjLatLngs[0];
var end = arrObjLatLngs[arrObjLatLngs.length - 1];
var base = new google.maps.LatLng(52.781048888889, -1.2110222222222546);
var service = new google.maps.DistanceMatrixService();
service.getDistanceMatrix(
{
origins: [base],
destinations: [start, end],
travelMode: google.maps.TravelMode.DRIVING,
unitSystem: google.maps.DirectionsUnitSystem.METRIC,
avoidHighways: false,
avoidTolls: false
}, callback);
function callback(response, status) {
if (status == google.maps.DistanceMatrixStatus.OK) {
var origins = response.originAddresses;
var destinations = response.destinationAddresses;
for (var i = 0; i < origins.length; i++) {
var results = response.rows[i].elements;
for (var j = 0; j < results.length; j++) {
var element = results[j];
var distance = Math.round(parseFloat(element.distance.text));
alert(distance);
var duration = element.duration.text;
var from = origins[i];
var to = destinations[j];
}
}
}
}
When I alert($distance); I get two responses, e.g. 15 and 10
What I need is two seperate values, i.e. if I alert($runinPickup); I get 15 , if I alert alert($runinDestination); I get 10.
I thought they were in an array so I have tried alert(distance[0]) but this comes back as undefined.
Do I have to do two seperate getDistanceMatrix requests or is there a way to seperate the two values?
I have tried with the following code to properly display the direction line between 2 cities, however the line between 2 cities is not properly set. If distance between 2 cities is long enough then one city icon is displayed and to get see the other city icon displayed on the map, I have to go to the place.
please check the code below:
void ShowDirection::on_webView_loadFinished(bool)
{
QStringList scriptStr;
scriptStr
<< "var map = new google.maps.Map2(document.getElementById(\"map\"));"
<< "var bounds = new GLatLngBounds();"
<< "var colors = ['#FF0000'];"
<< "var baseStartIcon = new GIcon();"
<< "var baseEndIcon = new GIcon();"
<< "baseStartIcon.image = 'qrc:images/green.png';"
<< "baseEndIcon.image = 'qrc:images/red.png';"
<< "baseStartIcon.iconSize = new GSize(37, 37);"
<< "baseEndIcon.iconSize = new GSize(37, 64);"
<< "baseStartIcon.iconAnchor = new GPoint(18, 37);"
<< "baseEndIcon.iconAnchor = new GPoint(0, 64);"
<< "map.addControl(new GLargeMapControl());"
<< "var directions = new GDirections();"
<< "GEvent.addListener(directions,\"load\", function() {"
<< "var poly=directions.getPolyline();"
<< "map.setCenter(poly.getVertex(0),8);"
<< "map.addOverlay(new GMarker(poly.getVertex(0),baseStartIcon));"
<< "map.addOverlay(new GMarker(poly.getVertex(poly.getVertexCount()-1),baseEndIcon));"
// << "bounds.extend(poly.getBounds());"
// << "map.setCenter(bounds.getCenter(),map.getBoundsZoomLevel(bounds));"
<< "poly.setStrokeStyle({color:colors[0],weight:3,opacity: 0.7});"
<< "map.addOverlay(poly);"
<< "});"
<< QString ("directions.loadFromWaypoints([\"%1\",\"%2\"],{getPolyline:true});")
.arg(fromCurrentLocationShowDirection)
.arg(toEventLocationShowDirection);
ui->webView->page()->mainFrame()->evaluateJavaScript(scriptStr.join("\n"));
}
How to bound the 2 city locations direction line on the map, so that whatever may be the distance both location directions display on the map.
Thanks...
After modifying the code it's working now:
void ShowDirection::on_webView_loadFinished(bool)
{
QStringList scriptStr;
scriptStr
<< "var map = new google.maps.Map2(document.getElementById(\"map\"));"
<< "var bounds = new GLatLngBounds();"
<< "var colors = ['#FF0000'];"
<< "var baseStartIcon = new GIcon();"
<< "var baseEndIcon = new GIcon();"
<< "baseStartIcon.image = 'qrc:images/currentlocation-green.png';"
<< "baseEndIcon.image = 'qrc:images/iPhone_redFlag-events.png';"
<< "baseStartIcon.iconSize = new GSize(37, 37);"
<< "baseEndIcon.iconSize = new GSize(37, 64);"
<< "baseStartIcon.iconAnchor = new GPoint(18, 37);"
<< "baseEndIcon.iconAnchor = new GPoint(0, 64);"
<< "map.addControl(new GLargeMapControl());"
<< "var directions = new GDirections();"
<< "GEvent.addListener(directions,\"load\", function() {"
<< "var poly=directions.getPolyline();"
<< "bounds.extend(poly.getVertex(0));"
<< "bounds.extend(poly.getVertex(poly.getVertexCount()-1)); "
<< "map.setCenter(bounds.getCenter(),map.getBoundsZoomLevel(bounds));"
<< "map.addOverlay(new GMarker(poly.getVertex(0),baseStartIcon));"
<< "map.addOverlay(new GMarker(poly.getVertex(poly.getVertexCount()-1),baseEndIcon));"
<< "poly.setStrokeStyle({color:colors[0],weight:3,opacity: 0.7});"
<< "map.addOverlay(poly);"
<< "});"
<< QString ("directions.loadFromWaypoints([\"%1\",\"%2\"],{getPolyline:true});")
.arg(fromCurrentLocationShowDirection)
.arg(toEventLocationShowDirection);
ui->webView->page()->mainFrame()->evaluateJavaScript(scriptStr.join("\n"));
}
This would be useful for anybody, who wants to display GDirection with colorful poly line with custom Icons.