/* * Open Source Routing Machine (OSRM) - Web (GUI) Interface * Copyright (C) Dennis Luxen, 2011 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU AFFERO General Public License as published by * the Free Software Foundation; either version 3 of the License, or * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * or see http://www.gnu.org/licenses/agpl.txt. */ /** * Title: ViaRoute.js * Description: JS file for routing with via points * * @author Dennis Luxen, luxen@kit.edu * @version 0.1 June 2011 * */ //====================== // OBJECTS //global timestamp to control frequency of mouse->route distance computations var timestamp = (new Date()).getTime(); var ISCOMPUTINGDISTANCE = false; var ISCALCULATINGVIA = false; //var HASDELETEDTEMPORARYVIAROUTE = false; var nearestPoint = new OpenLayers.Geometry.Point(1,1); var nearestSegmentIndex = null; var viaPointsVector = []; var p0; var temporaryViaStyle = { fillColor: "#ffffff", strokeColor: "#33ff33", strokeOpacity: 0.6, strokeWidth: 2, pointRadius: 4, pointerEvents: "visiblePainted" }; var permanentViaStyle = { fillColor: "#ffffff", strokeColor: "#0033ff", strokeOpacity: 0.6, strokeWidth: 2, pointRadius: 4, pointerEvents: "visiblePainted" }; //====================== //FUNCTIONS function distanceBetweenPoint(x1, y1, x2, y2) { var a = x1 - x2; var b = y1 - y2; return Math.sqrt(a*a + b*b); } /* Distance from a point to a line or segment. x point's x coord y point's y coord x0 x coord of the line's A point y0 y coord of the line's A point x1 x coord of the line's B point y1 y coord of the line's B point o specifies if the distance should respect the limits of the segment (overLine = true) or if it should consider the segment as an infinite line (overLine = false), if false returns the distance from the point to the line, otherwise the distance from the point to the segment */ function dotLineLength(x, y, x0, y0, x1, y1, o){ function lineLength(x, y, x0, y0){ return Math.sqrt((x -= x0) * x + (y -= y0) * y); } if(o && !(o = function(x, y, x0, y0, x1, y1){ if(!(x1 - x0)) return {x: x0, y: y}; else if(!(y1 - y0)) return {x: x, y: y0}; var left, tg = -1 / ((y1 - y0) / (x1 - x0)); return {x: left = (x1 * (x * tg - y + y0) + x0 * (x * - tg + y - y1)) / (tg * (x1 - x0) + y0 - y1), y: tg * left - tg * x + y}; }(x, y, x0, y0, x1, y1), o.x >= Math.min(x0, x1) && o.x <= Math.max(x0, x1) && o.y >= Math.min(y0, y1) && o.y <= Math.max(y0, y1))){ var l1 = lineLength(x, y, x0, y0), l2 = lineLength(x, y, x1, y1); return l1 > l2 ? l2 : l1; } else { var a = y0 - y1, b = x1 - x0, c = x0 * y1 - y0 * x1; return Math.abs(a * x + b * y + c) / Math.sqrt(a * a + b * b); } }; function distanceToRoute(pixel) { if(!isStartPointSet || !isEndPointSet){ return; } if(ISDRAGGING) { return; } if(ISCOMPUTINGDISTANCE) { return; } var tempTime = (new Date()).getTime() if( tempTime - timestamp < 25) { return; } timestamp = tempTime; if(ISCALCULATING) { var viaPointFeature = dragLayer.getFeatureBy("name", 'via'); if(viaPointFeature != null) { dragLayer.removeFeatures(viaPointFeature); viaPointFeature.destroy; } return; } if(pixel == null) { pixel.x = window.event.clientX; pixel.y = window.event.clientY; } var newLonLat = map.getLonLatFromPixel(pixel); var minimumDist = 20*Math.pow(2,(19- this.map.getZoom())); var minimumDistToVia = 20*Math.pow(2,(19- this.map.getZoom())); var indexToInsertViaPoint = 0; var startFeat = getMarkerByName('start'); var endFeat = getMarkerByName('end'); var from = new OpenLayers.LonLat(startFeat.geometry.x,startFeat.geometry.y).transform(EPSG_900913, EPSG_4326); var to = new OpenLayers.LonLat(endFeat.geometry.x,endFeat.geometry.y).transform(EPSG_900913, EPSG_4326); var mapExtent = map.getExtent(); for(var i = 0; i < allRoutePoints.length-1; i++) { var p1 = allRoutePoints[i]; var p2 = allRoutePoints[i+1]; if(p1.x < mapExtent.left || p1.x > mapExtent.right || p1.y < mapExtent.bottom || p1.y > mapExtent.top) { if(p2.x < mapExtent.left || p2.x > mapExtent.right || p2.y < mapExtent.bottom || p2.y > mapExtent.top) { continue; } } //check if previous segment is closest to via point //if yes, then we have passed a via point. if(viaPointsVector.length > indexToInsertViaPoint && p0) { var viaPoint = new OpenLayers.LonLat(viaPointsVector[indexToInsertViaPoint][1],viaPointsVector[indexToInsertViaPoint][0]).transform(EPSG_4326, EPSG_900913); var dist2 = dotLineLength(viaPoint.lon, viaPoint.lat, p1.x, p1.y, p0.x, p0.y, true) if( 0 == dist2 ) { indexToInsertViaPoint++; } var tmpDist = distanceBetweenPoint(viaPoint.lat, viaPoint.lon, newLonLat.lat, newLonLat.lon); if(tmpDist < minimumDistToVia) { minimumDistToVia = tmpDist; } } //continue if point out of view dist = dotLineLength(newLonLat.lon, newLonLat.lat, p1.x, p1.y, p2.x, p2.y, true); if(dist <= minimumDist) { nearestSegmentIndex = indexToInsertViaPoint; minimumDist = dist; var lonDiff = (p2.x - p1.x) var m = (p2.y - p1.y) / lonDiff; var b = p1.y - (m * p1.x); var m2 = m*m; var x = (m * newLonLat.lat + newLonLat.lon - m * b) / (m2 + 1); var y = (m2 * newLonLat.lat + m * newLonLat.lon + b) / (m2 + 1); var r = (x - p1.x) / lonDiff; if ( r <= 1 && r >= 0 ) { nearestPoint.x = x; nearestPoint.y = y; } else { if( r < 0 ) { nearestPoint.x = p1.x; nearestPoint.y = p1.y; } if ( r > 1 ) { nearestPoint.x = p2.x; nearestPoint.y = p2.y; } } if(p2.x == p1.x) { nearestPoint.x = p1.x; nearestPoint.y = newLonLat.lat; } } } var zoomFactor = Math.pow(2,(19- this.map.getZoom())); var viaPointFeature = dragLayer.getFeatureBy("name", 'via'); if(viaPointFeature != null) { dragLayer.removeFeatures(viaPointFeature); viaPointFeature.destroy; } //too close to start or destination? if(Math.min(distanceBetweenPoint(startFeat.geometry.x, startFeat.geometry.y, newLonLat.lon, newLonLat.lat), distanceBetweenPoint(endFeat.geometry.x, endFeat.geometry.y, newLonLat.lon, newLonLat.lat)) < 10*zoomFactor) { ISCOMPUTINGDISTANCE = false; return; } //Are we close to the route but sufficiently far away from any existing via point? if(minimumDist < 10*zoomFactor && minimumDistToVia > 5*zoomFactor) { viaPointFeature = new OpenLayers.Feature.Vector( OpenLayers.Geometry.Polygon.createRegularPolygon( nearestPoint, (1.5*zoomFactor), 15, 0 ), null, temporaryViaStyle ); viaPointFeature.name = "via"; dragLayer.addFeatures( viaPointFeature ); //console.log('nearestSegmentIndex: ' + nearestSegmentIndex); } ISCOMPUTINGDISTANCE = false; p0 = p1; } function computeViaRoute(pixel, isTemporary, skipViaPointsIndex) { if(ISCALCULATINGVIA && isTemporary) return; if( nearestSegmentIndex == null ) { //console.log('nearestSegmentIndex: ' + nearestSegmentIndex); return; } if(isTemporary == null) { isTemporary = false; } //So, we jumped all fences ISCALCULATINGVIA = true; var startFeat = getMarkerByName('start'); var endFeat = getMarkerByName('end'); var from = new OpenLayers.LonLat(startFeat.geometry.x,startFeat.geometry.y).transform(EPSG_900913, EPSG_4326); var to = new OpenLayers.LonLat(endFeat.geometry.x,endFeat.geometry.y).transform(EPSG_900913, EPSG_4326); var coordinate = map.getLonLatFromPixel(pixel); var via = coordinate.transform(EPSG_900913, EPSG_4326); var viaNodeInserted = false; var newURL = HOST_ROUTING_URL + "?start="+from.lat + ',' + from.lon + '&dest=' + to.lat + ',' + to.lon; newURL += '&geomformat=cmp'; for(var i = 0; i < viaPointsVector.length; i++) { if(i == nearestSegmentIndex) { //insert new via node here newURL += '&via=' + via.lat + ',' + via.lon; viaNodeInserted = true; } if(skipViaPointsIndex == i) continue; newURL += '&via=' + viaPointsVector[i][0] + ',' + viaPointsVector[i][1]; } if(false == viaNodeInserted) { newURL += '&via=' + via.lat + ',' + via.lon; } newURL += '&output=json' + '&z=' + this.map.getZoom(); if(!isTemporary) { newURL += '&instructions=true&jsonp=showResultsRoute'; } else { newURL += '&instructions=false&jsonp=showResultsViaRoute'; } //console.log(newURL); var script = document.createElement('script'); script.id = 'JSONP'; script.type = 'application/javascript'; script.src = newURL; document.body.appendChild(script); } function showResultsViaRoute(response) { if (response) { var viaRouteVector = [];     // now with compression of the route geometry        var geometry = decodeRouteGeometry(response.route_geometry, 5);       var lengthOfArray = geometry.length;        var points = [];        points.length = lengthOfArray;        //Create Route Layer for Display        for (var i = 0; i < lengthOfArray; i++) {            var point = new OpenLayers.Geometry.Point(geometry[i][1], geometry[i][0]).transform(EPSG_4326, EPSG_900913);            allRoutePoints.push(point); points.push(point);             if(i % 1024 == 0 && i>0 || i==lengthOfArray-1){ /* var feature1 = new OpenLayers.Feature.Vector(new OpenLayers.Geometry.LineString(points), null, { strokeColor: "#006600", strokeOpacity: 1, strokeWidth: 9 }); */ var feature = new OpenLayers.Feature.Vector(new OpenLayers.Geometry.LineString(points), null, { strokeColor: "#aaffaa", strokeOpacity: 0.8, strokeWidth: 7 }); // viaRouteVector.push(feature1); viaRouteVector.push(feature); points = []; points.push(point); }        } vectorLayerViaRoute.removeFeatures(vectorLayerViaRoute.features); for(var i = 0; i< viaRouteVector.length; i++) { vectorLayerViaRoute.addFeatures(viaRouteVector[i]); } viaRouteVector.length = 0; ISCALCULATINGVIA = false; } } function paintViaPoints() { //remove all previous via points var featuresToRemove = []; for(var i = 0; i < dragLayer.features.length; i++) { if(dragLayer.features[i].name == "viapoint" || "via" == dragLayer.features[i].name ) { featuresToRemove.push(dragLayer.features[i]); } } dragLayer.removeFeatures(featuresToRemove); featuresToRemove.length = 0; //paint all via points var zoomFactor = Math.pow(2,(19- this.map.getZoom())); //console.log('painting ' + viaPointsVector.length + ' via points'); for(var i = 0; i < viaPointsVector.length; i++) { //console.log(viaPointsVector[i]); var viapoint = new OpenLayers.Geometry.Point( viaPointsVector[i][1], viaPointsVector[i][0]); viapoint.transform(EPSG_4326, EPSG_900913); var viaPointFeature = new OpenLayers.Feature.Vector( OpenLayers.Geometry.Polygon.createRegularPolygon( viapoint, (1.5*zoomFactor), 20, 0 ), null, permanentViaStyle ); viaPointFeature.name = "viapoint"; viaPointFeature.viaIndex = i; dragLayer.addFeatures( viaPointFeature ) } }