Last active
August 29, 2015 14:13
-
-
Save pgiraud/b04834295af5fc250f2a to your computer and use it in GitHub Desktop.
OpenLayers3 - Show destination
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <!DOCTYPE html> | |
| <html> | |
| <head> | |
| <title>OpenLayers3 Map Rotation with device compass</title> | |
| <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width"> | |
| <link rel="stylesheet" href="style.css"> | |
| <link rel="stylesheet" href="http://openlayers.org/en/master/css/ol.css" type="text/css"> | |
| <script src="//code.jquery.com/jquery-1.11.2.min.js"></script> | |
| <script src="http://openlayers.org/en/master/build/ol.js"></script> | |
| </head> | |
| <body> | |
| <div id="map"></div> | |
| <button id="rotate">rotate</button> | |
| </body> | |
| <script src="script.js"></script> | |
| <script src="intersections.js"></script> | |
| </html> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // code adapted from | |
| // http://martin-thoma.com/how-to-check-if-two-line-segments-intersect/ | |
| var EPSILON = 0.000001; | |
| /** | |
| * Calculate the cross product of two points. | |
| * @param a first point | |
| * @param b second point | |
| * @return the value of the cross product | |
| */ | |
| function crossProduct(a, b) { | |
| return a[0] * b[1] - b[0] * a[1]; | |
| } | |
| /** | |
| * Check if bounding boxes do intersect. If one bounding box | |
| * touches the other, they do intersect. | |
| * @param a first bounding box | |
| * @param b second bounding box | |
| * @return <code>true</code> if they intersect, | |
| * <code>false</code> otherwise. | |
| */ | |
| function doBoundingBoxesIntersect(a, b) { | |
| return a[0][0] <= b[1][0] && a[1][0] >= b[0][0] && a[0][1] <= b[1][1] && | |
| a[1][1] >= b[0][1]; | |
| } | |
| /** | |
| * Checks if a Point is on a line | |
| * @param a line (interpreted as line, although given as line | |
| * segment) | |
| * @param b point | |
| * @return <code>true</code> if point is on line, otherwise | |
| * <code>false</code> | |
| */ | |
| function isPointOnLine(a, b) { | |
| // Move the image, so that a[0] is on (0|0) | |
| var aTmp = [[0,0], [a[1][0] - a[0][0],a[1][1] - a[0][1]]]; | |
| var bTmp = [b[0] - a[0][0], b[1] - a[0][1]]; | |
| var r = crossProduct(aTmp[1], bTmp); | |
| return Math.abs(r) < EPSILON; | |
| } | |
| /** | |
| * Checks if a point is right of a line. If the point is on the | |
| * line, it is not right of the line. | |
| * @param a line segment interpreted as a line | |
| * @param b the point | |
| * @return <code>true</code> if the point is right of the line, | |
| * <code>false</code> otherwise | |
| */ | |
| function isPointRightOfLine(a, b) { | |
| // Move the image, so that a[0] is on (0|0) | |
| var aTmp = [[0,0], [a[1][0] - a[0][0], a[1][1] - a[0][1]]]; | |
| var bTmp = [b[0] - a[0][0], b[1] - a[0][1]]; | |
| return crossProduct(aTmp[1], bTmp) < 0; | |
| } | |
| /** | |
| * Check if line segment first touches or crosses the line that is | |
| * defined by line segment second. | |
| * | |
| * @param first line segment interpreted as line | |
| * @param second line segment | |
| * @return <code>true</code> if line segment first touches or | |
| * crosses line second, | |
| * <code>false</code> otherwise. | |
| */ | |
| function lineSegmentTouchesOrCrossesLine(a, b) { | |
| return isPointOnLine(a, b[0]) || isPointOnLine(a, b[1]) || | |
| (isPointRightOfLine(a, b[0]) ^ isPointRightOfLine(a, b[1])); | |
| } | |
| function getBoundingBox(a) { | |
| return [[Math.min(a[0][0], a[1][0]), Math.min(a[0][1], a[1][1])], | |
| [Math.max(a[0][0], a[1][0]), Math.max(a[0][1], a[1][1])]]; | |
| } | |
| /** | |
| * Check if line segments intersect | |
| * @param a first line segment | |
| * @param b second line segment | |
| * @return <code>true</code> if lines do intersect, | |
| * <code>false</code> otherwise | |
| */ | |
| function doLinesIntersect(a, b) { | |
| var box1 = getBoundingBox(a); | |
| var box2 = getBoundingBox(b); | |
| return doBoundingBoxesIntersect(box1, box2) && | |
| lineSegmentTouchesOrCrossesLine(a, b) && | |
| lineSegmentTouchesOrCrossesLine(b, a); | |
| } | |
| /** You know that lines a and b have an intersection and now you | |
| want to get it! | |
| */ | |
| function getIntersection(a, b) { | |
| //the intersection [(x1,y1), (x2, y2)] | |
| //it might be a line or a single point. If it is a line, | |
| //then x1 = x2 and y1 = y2 | |
| var x1, y1, x2, y2; | |
| var tmp; | |
| var m, t; | |
| if (a[0][0] == a[1][0]) { | |
| // Case (A) | |
| // As a is a perfect vertical line, it cannot be represented | |
| // nicely in a mathematical way. But we directly know that | |
| // | |
| x1 = a[0][0]; | |
| x2 = x1; | |
| if (b[0][0] == b[1][0]) { | |
| // Case (AA): all x are the same! | |
| // Normalize | |
| if(a[0][1] > a[1][1]) { | |
| a = [a[1], a[0]]; | |
| } | |
| if(b[0][1] > b[1][1]) { | |
| b = [b[1], b[0]]; | |
| } | |
| if(a[0][1] > b[0][1]) { | |
| tmp = a; | |
| a = b; | |
| b = tmp; | |
| } | |
| // Now we know that the y-value of a[0] is the | |
| // lowest of all 4 y values | |
| // this means, we are either in case (AAA): | |
| // a: x--------------x | |
| // b: x---------------x | |
| // or in case (AAB) | |
| // a: x--------------x | |
| // b: x-------x | |
| // in both cases: | |
| // get the relavant y intervall | |
| y1 = b[0][1]; | |
| y2 = Math.min(a[1][1], b[1][1]); | |
| } else { | |
| // Case (AB) | |
| // we can mathematically represent line b as | |
| // y = m*x + t <=> t = y - m*x | |
| // m = (y1-y2)/(x1-x2) | |
| m = (b[0][1] - b[1][1]) / (b[0][0] - b[1][0]); | |
| t = b[0][1] - m*b[0][0]; | |
| y1 = m*x1 + t; | |
| y2 = y1; | |
| } | |
| } else if (b[0][0] == b[1][0]) { | |
| // Case (B) | |
| // essentially the same as Case (AB), but with | |
| // a and b switched | |
| x1 = b[0][0]; | |
| x2 = x1; | |
| tmp = a; | |
| a = b; | |
| b = tmp; | |
| m = (b[0][1] - b[1][1]) / (b[0][0] - b[1][0]); | |
| t = b[0][1] - m*b[0][0]; | |
| y1 = m*x1 + t; | |
| y2 = y1; | |
| } else { | |
| // Case (C) | |
| // Both lines can be represented mathematically | |
| var ma, mb, ta, tb; | |
| ma = (a[0][1] - a[1][1]) / (a[0][0] - a[1][0]); | |
| mb = (b[0][1] - b[1][1]) / (b[0][0] - b[1][0]); | |
| ta = a[0][1] - ma*a[0][0]; | |
| tb = b[0][1] - mb*b[0][0]; | |
| if (ma == mb) { | |
| // Case (CA) | |
| // both lines are in parallel. As we know that they | |
| // intersect, the intersection could be a line | |
| // when we rotated this, it would be the same situation | |
| // as in case (AA) | |
| // Normalize | |
| if(a[0][0] > a[1][0]) { | |
| a = [a[1], a[0]]; | |
| } | |
| if(b[0][0] > b[1][0]) { | |
| b = [b[1], b[0]]; | |
| } | |
| if(a[0][0] > b[0][0]) { | |
| tmp = a; | |
| a = b; | |
| b = tmp; | |
| } | |
| // get the relavant x intervall | |
| x1 = b[0][0]; | |
| x2 = Math.min(a[1][0], b[1][0]); | |
| y1 = ma*x1+ta; | |
| y2 = ma*x2+ta; | |
| } else { | |
| // Case (CB): only a point as intersection: | |
| // y = ma*x+ta | |
| // y = mb*x+tb | |
| // ma*x + ta = mb*x + tb | |
| // (ma-mb)*x = tb - ta | |
| // x = (tb - ta)/(ma-mb) | |
| x1 = (tb-ta)/(ma-mb); | |
| y1 = ma*x1+ta; | |
| x2 = x1; | |
| y2 = y1; | |
| } | |
| } | |
| return [[x1,y1], [x2,y2]]; | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // Code goes here | |
| var projection = ol.proj.get('EPSG:3857'); | |
| var view = new ol.View({ | |
| center: [0, 0], | |
| projection: projection, | |
| extent: projection.getExtent(), | |
| zoom: 2 | |
| }); | |
| var map = new ol.Map({ | |
| layers: [ | |
| new ol.layer.Tile({ | |
| source: new ol.source.OSM() | |
| }) | |
| ], | |
| target: 'map', | |
| controls: ol.control.defaults({ | |
| attributionOptions: /** @type {olx.control.AttributionOptions} */ ({ | |
| collapsible: false | |
| }), | |
| rotateOptions: { | |
| autoHide: false | |
| } | |
| }), | |
| view: view | |
| }); | |
| var rotateMap = false; | |
| $('#rotate').on('click', function() { | |
| $(this).toggleClass('active'); | |
| rotateMap = !rotateMap; | |
| }); | |
| var deviceOrientation = new ol.DeviceOrientation(); | |
| deviceOrientation.on(['change:heading'], function(event) { | |
| var heading = event.target.getHeading() || 0; | |
| var viewRotation = 0; | |
| var markerRotation = 0; | |
| if (rotateMap) { | |
| viewRotation = -heading; | |
| view.setCenter(geolocation.getPosition()); | |
| } else { | |
| markerRotation = heading; | |
| } | |
| view.setRotation(viewRotation); | |
| positionMarkerIcon.setRotation(markerRotation) | |
| positionFeature.setStyle(new ol.style.Style({ | |
| image: positionMarkerIcon | |
| })); | |
| }); | |
| deviceOrientation.setTracking(true); | |
| var geolocation = new ol.Geolocation({ | |
| projection: view.getProjection(), | |
| tracking: true | |
| }); | |
| geolocation.once('change:position', function() { | |
| view.setCenter(geolocation.getPosition()); | |
| view.setZoom(12); | |
| }); | |
| geolocation.on('change:position', function() { | |
| var coords = [geolocation.getPosition(), target.getGeometry().getCoordinates()]; | |
| lineToTarget.setGeometry(new ol.geom.LineString(coords)); | |
| }); | |
| var positionFeature = new ol.Feature(); | |
| positionFeature.bindTo('geometry', geolocation, 'position') | |
| .transform(function() {}, function(coordinates) { | |
| return coordinates ? new ol.geom.Point(coordinates) : null; | |
| }); | |
| var positionMarkerIcon = new ol.style.Icon({ | |
| src: "http://openlayers.org/en/master/examples/data/geolocation_marker_heading.png", | |
| anchor: [11, 24], | |
| anchorXUnits: 'pixels', | |
| anchorYUnits: 'pixels' | |
| }); | |
| var featuresOverlay = new ol.FeatureOverlay({ | |
| map: map, | |
| features: [positionFeature] | |
| }); | |
| // destination: the point the user is supposed to go to | |
| var target = new ol.Feature({ | |
| geometry: new ol.geom.Point( | |
| ol.proj.transform([Math.random() * 360 - 180, Math.random() * 180 - 90], | |
| 'EPSG:4326', 'EPSG:3857')) | |
| }); | |
| var style = new ol.style.Style({ | |
| image: new ol.style.Circle({ | |
| radius: 5, | |
| fill: new ol.style.Fill({color: 'red'}), | |
| stroke: new ol.style.Stroke({color: 'black', width: 1}) | |
| }) | |
| }); | |
| target.setStyle(style); | |
| featuresOverlay.addFeature(target); | |
| // the line to target | |
| var lineToTarget = new ol.Feature(); | |
| lineToTarget.setStyle(new ol.style.Style({ | |
| stroke: new ol.style.Stroke({ | |
| color: "white", | |
| lineDash: [5, 5], | |
| width: 2 | |
| }) | |
| })); | |
| featuresOverlay.addFeature(lineToTarget); | |
| var intersectionFeature = new ol.Feature(); | |
| featuresOverlay.addFeature(intersectionFeature); | |
| intersectionIcon = new ol.style.Icon({ | |
| src: "arrow_skip_up.png", | |
| anchor: [24, 0], | |
| anchorXUnits: 'pixels', | |
| anchorYUnits: 'pixels' | |
| }); | |
| intersectionFeature.setStyle(new ol.style.Style({ | |
| image: intersectionIcon | |
| })); | |
| view.on('propertychange', function() { | |
| if (!lineToTarget.getGeometry()) { | |
| return; | |
| } | |
| var seg = lineToTarget.getGeometry().getCoordinates(); | |
| // the polygon representing the current visible area | |
| // note: it's not really an extent | |
| var size = map.getSize(); | |
| var width = size[0]; | |
| var height = size[1]; | |
| var box = [ | |
| map.getCoordinateFromPixel([0, 0]), | |
| map.getCoordinateFromPixel([width, 0]), | |
| map.getCoordinateFromPixel([width, height]), | |
| map.getCoordinateFromPixel([0, height]), | |
| map.getCoordinateFromPixel([0, 0]) | |
| ]; | |
| var slope = Math.atan2(seg[1][1] - seg[0][1], seg[1][0] - seg[0][0]); | |
| intersectionIcon.setRotation(Math.PI / 2 - slope + view.getRotation()); | |
| // calculate the intersection between line to destination and current visible | |
| // extent | |
| var len = box.length; | |
| for (var i = 0; i < len - 1; i++) { | |
| var a = [seg[0], seg[1]]; | |
| var b = [box[i], box[i + 1]]; | |
| if (doLinesIntersect(a, b)) { | |
| intersection = getIntersection(a, b); | |
| var point = new ol.geom.Point([intersection[0][0], intersection[0][1]]); | |
| //featuresOverlay.addFeature(new ol.Feature(point)); | |
| intersectionFeature.setGeometry(point); | |
| if (intersection) { | |
| continue; | |
| } | |
| } | |
| } | |
| //var intersection = segmentExtentIntersection(segment, extent); | |
| //intersectionFeature.setGeometry(new ol.geom.Point(intersection)); | |
| }); | |
| // convert degrees to radians | |
| function degToRad(deg) { | |
| return deg * Math.PI * 2 / 360; | |
| } | |
| // convert radians to degrees | |
| function radToDeg(rad) { | |
| return rad * 360 / (Math.PI * 2); | |
| } | |
| /** | |
| * Calculates the intersection between segment and extent (bbox) | |
| * @param seg the segment | |
| * @param extent the extent | |
| * @return the intersection coordinates or false | |
| */ | |
| //function segmentExtentIntersection(seg, extent) { | |
| //var minx = extent[0]; | |
| //var miny = extent[1]; | |
| //var maxx = extent[2]; | |
| //var maxy = extent[3]; | |
| //var extentSegments = [ | |
| //[[minx, miny], [minx, maxy]], | |
| //[[minx, maxy], [maxx, maxy]], | |
| //[[maxx, maxy], [maxx, miny]], | |
| //[[maxx, miny], [minx, miny]] | |
| //]; | |
| //var intersection = false; | |
| //for (var i=0; i < extentSegments.length; i++) { | |
| //var a = [[seg[0][0], seg[0][1]], [seg[1][0], seg[1][1]]]; | |
| //var b = extentSegments[i]; | |
| //if (doLinesIntersect(a, b)) { | |
| //intersection = getIntersection(a, b); | |
| //return [intersection[0][0], intersection[0][1]]; | |
| //} | |
| //} | |
| //return intersection; | |
| //} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /* Styles go here */ | |
| html, body, #map { | |
| margin: 0; | |
| padding: 0; | |
| width: 100%; | |
| height: 100%; | |
| } | |
| #rotate { | |
| position: absolute; | |
| left: 10px; | |
| bottom: 10px; | |
| z-index: 2; | |
| } | |
| #rotate.active { | |
| background-color: yellow; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
