Note that there are some explanatory texts on larger screens.

plurals
  1. POGoogle maps api parallel path lines
    primarykey
    data
    text
    <p>I am working on a sort of itinerary mapper for packaged vacations, and I'm really happy with what I've done so far; I have the directions api implemented with a custom renderer, so I can take driving directions, and plot my own polyline complete with directional arrows that aren't google's awful ones spaced along the path. I am not exactly a math expert, and I am trying to figure out how I could make a path parallel to another path. For example, the itinerary goes from city 1 to city 2, and then back to city 1. </p> <p>I want to offset the trip back to city 1's polyline, so that it mirrors the path, but travels parallel to it. Ideally, I would like to when I create the path, check for intersecting points in other paths, and if any are found, offset the path at those points only. This would be a better implementation, because you could for instance parallel the path only where it happens to intersect another one, like when it meets another path only for a short time.</p> <p>I found this code for API2 from bill chadwick</p> <p>The link is here: <a href="http://wtp2.appspot.com/ParallelLines.htm" rel="nofollow noreferrer">http://wtp2.appspot.com/ParallelLines.htm</a></p> <p>Update: Somehow managed to convert this old v2 script to get it working in v3, but I'm experiencing some troubles...</p> <p>It is more than doubling the original number of points, and following the path, but really throwing them in randomly. Screenshot here:</p> <p><img src="https://i.stack.imgur.com/NBE6S.jpg" alt="Google maps screenshot"></p> <p>The class I converted is here:</p> <pre><code>function BDCCParallelLines(points, color, weight, opacity, opts, gapPx) { console.log('Pllel COnstructor Initialized'); this.gapPx = gapPx; this.points = points; this.color = color; this.weight = weight; this.opacity = opacity; this.opts = opts; this.line1 = null; this.line2 = null; this.lstnZoom = null; } BDCCParallelLines.prototype = new google.maps.OverlayView(); BDCCParallelLines.prototype.onAdd = function() { console.log('Pllel Initialized'); this.prj = map.getProjection(); var self = this; this.lstnZoom = google.maps.event.addListener(map, "zoom_changed", function() { self.recalc(); }); this.recalc();//first draw } BDCCParallelLines.prototype.onRemove = function() { if(this.line2) this.line2.setMap(null); if(this.line1) this.line1.setMap(null); if(this.lstnZoom != null) google.maps.event.removeListener(this.lstnZoom); } BDCCParallelLines.prototype.copy = function() { return new BDCCParallelLines(this.points,this.color,this.weight,this.opacity,this.opts,this.gapPx); } BDCCParallelLines.prototype.draw = function(force) { return; //do nothing } /** * @param {google.maps.Map} map * @param {google.maps.LatLng} latlng * @param {int} z * @return {google.maps.Point} */ BDCCParallelLines.prototype.latLngToPoint = function(latlng, z){ var normalizedPoint = map.getProjection().fromLatLngToPoint(latlng); // returns x,y normalized to 0~255 var scale = Math.pow(2, z); var pixelCoordinate = new google.maps.Point(normalizedPoint.x * scale, normalizedPoint.y * scale); return pixelCoordinate; }; /** * @param {google.maps.Map} map * @param {google.maps.Point} point * @param {int} z * @return {google.maps.LatLng} */ BDCCParallelLines.prototype.pointToLatlng = function(point, z){ var scale = Math.pow(2, z); var normalizedPoint = new google.maps.Point(point.x / scale, point.y / scale); var latlng = map.getProjection().fromPointToLatLng(normalizedPoint); return latlng; }; BDCCParallelLines.prototype.recalc = function() { var distallowance; console.log('recalc called'); var zoom = map.getZoom(); distallowance = 1.6; if(zoom &gt; 6){ distallowance = 1.3; if(zoom &gt; 9){ distallowance = .7; if( zoom &gt; 13){ distallowance = .2; if( zoom &gt; 15){ distallowance = .0001; } } } } console.log('Zoom Level: ' + zoom); console.log('Allowance = ' + distallowance); var pts1 = new Array();//left side of center //shift the pts array away from the centre-line by half the gap + half the line width var o = (this.gapPx + this.weight)/2; var p2l,p2r; for (var i=1; i&lt;this.points.length; i++){ var p1lm1; var p1rm1; var p2lm1; var p2rm1; var thetam1; var p1 = this.latLngToPoint(this.points[i-1], zoom) var p2 = this.latLngToPoint(this.points[i], zoom) var theta = Math.atan2(p1.x-p2.x,p1.y-p2.y); theta = theta + (Math.PI/2); var dl = Math.sqrt(((p1.x-p2.x)*(p1.x-p2.x))+((p1.y-p2.y)*(p1.y-p2.y))); if(theta &gt; Math.PI) theta -= Math.PI*2; var dx = Math.round(o * Math.sin(theta)); var dy = Math.round(o * Math.cos(theta)); var p1l = new google.maps.Point(p1.x+dx,p1.y+dy); var p1r = new google.maps.Point(p1.x-dx,p1.y-dy); p2l = new google.maps.Point(p2.x+dx,p2.y+dy); p2r = new google.maps.Point(p2.x-dx,p2.y-dy); if(i==1){ //first point pts1.push(this.pointToLatlng(p1l,zoom)); } else{ // mid this.points if(distbetweentwo(this.points[i-1], this.points[i]) &gt; distallowance){ if(theta == thetam1){ // adjacent segments in a straight line pts1.push(this.pointToLatlng(p1l,zoom)); } else{ var pli = this.intersect(p1lm1,p2lm1,p1l,p2l); var pri = this.intersect(p1rm1,p2rm1,p1r,p2r); var dlxi = (pli.x-p1.x); var dlyi = (pli.y-p1.y); var drxi = (pri.x-p1.x); var dryi = (pri.y-p1.y); var di = Math.sqrt((drxi*drxi)+(dryi*dryi)); var s = o / di; var dTheta = theta - thetam1; if(dTheta &lt; (Math.PI*2)) dTheta += Math.PI*2; if(dTheta &gt; (Math.PI*2)) dTheta -= Math.PI*2; if(dTheta &lt; Math.PI){ //intersect point on outside bend pts1.push(this.pointToLatlng(p2lm1,zoom)); pts1.push(this.pointToLatlng(new google.maps.Point(p1.x+(s*dlxi),p1.y+(s*dlyi)),zoom)); pts1.push(this.pointToLatlng(p1l,zoom)); } else if (di &lt; dl){ pts1.push(this.pointToLatlng(pli,zoom)); } else{ pts1.push(this.pointToLatlng(p2lm1,zoom)); pts1.push(this.pointToLatlng(p1l,zoom)); } } } else{ //console.log(distbetweentwo(this.points[i-1], this.points[i])); } } p1lm1 = p1l; p1rm1 = p1r; p2lm1 = p2l; p2rm1 = p2r; thetam1 = theta; //end loop } pts1.push(this.pointToLatlng(p2l,zoom));//final point // console.log(pts1); if(this.line1) this.line1.setMap(null); this.line1 = new google.maps.Polyline({ strokeColor: this.color, strokeOpacity: this.opacity, strokeWeight: this.weight, map: map, path: pts1 }); this.line1.setMap(map); } BDCCParallelLines.prototype.intersect = function(p0,p1,p2,p3) { // this function computes the intersection of the sent lines p0-p1 and p2-p3 // and returns the intersection point, var a1,b1,c1, // constants of linear equations a2,b2,c2, det_inv, // the inverse of the determinant of the coefficient matrix m1,m2; // the slopes of each line var x0 = p0.x; var y0 = p0.y; var x1 = p1.x; var y1 = p1.y; var x2 = p2.x; var y2 = p2.y; var x3 = p3.x; var y3 = p3.y; // compute slopes, note the cludge for infinity, however, this will // be close enough if ((x1-x0)!=0) m1 = (y1-y0)/(x1-x0); else m1 = 1e+10; // close enough to infinity if ((x3-x2)!=0) m2 = (y3-y2)/(x3-x2); else m2 = 1e+10; // close enough to infinity // compute constants a1 = m1; a2 = m2; b1 = -1; b2 = -1; c1 = (y0-m1*x0); c2 = (y2-m2*x2); // compute the inverse of the determinate det_inv = 1/(a1*b2 - a2*b1); // use Kramers rule to compute xi and yi var xi=((b1*c2 - b2*c1)*det_inv); var yi=((a2*c1 - a1*c2)*det_inv); return new google.maps.Point(Math.round(xi),Math.round(yi)); } </code></pre> <p>This is working to a point... It is working as well as the original implementation. The entire path is recalculated on a zoom basis, and I kind of hacked the function to skip very short paths(weird angles) at higher zoom levels, it more closely follows the path the more you zoom in. </p> <p>I would rather just have a fixed distance offset that is not recalculated, as it is pretty intensive... There are many programs which accomplish this feat, rhino3d, autocad, illustrator... I feel like it would be great for driving directions for google maps itself, an offsetting of the path so you can distinguish the return trip and the original trip.</p> <p>If anybody has done anything similar to this in JS even if its not for google maps specifically, I would love to see it. Links I am investigating:</p> <p><a href="http://processingjs.nihongoresources.com/bezierinfo/" rel="nofollow noreferrer">http://processingjs.nihongoresources.com/bezierinfo/</a></p> <p><a href="http://www.groupsrv.com/computers/about21532.html" rel="nofollow noreferrer">http://www.groupsrv.com/computers/about21532.html</a></p>
    singulars
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload