|
<!DOCTYPE html> |
|
<html> |
|
<head> |
|
<meta charset='utf-8' /> |
|
<title></title> |
|
<meta name='viewport' content='initial-scale=1,maximum-scale=1,user-scalable=no' /> |
|
<script src='https://api.tiles.mapbox.com/mapbox-gl-js/v0.12.0/mapbox-gl.js'></script> |
|
<script src='https://api.tiles.mapbox.com/mapbox.js/plugins/turf/v2.0.2/turf.min.js'></script> |
|
<link href='https://api.tiles.mapbox.com/mapbox-gl-js/v0.12.0/mapbox-gl.css' rel='stylesheet' /> |
|
<style> |
|
body { margin:0; padding:0; } |
|
#map { position:absolute; top:0; bottom:0; width:100%; } |
|
</style> |
|
</head> |
|
<body> |
|
|
|
<div id='map'></div> |
|
<script> |
|
|
|
/* Interpolate function borrowed from https://github.com/pelias/line-interpolate-points all rights reserved */ |
|
|
|
/** |
|
* @param {Point} pt1 |
|
* @param {Point} pt1 |
|
* @return number The Euclidean distance between `pt1` and `pt2`. |
|
*/ |
|
function distance( pt1, pt2 ){ |
|
var deltaX = pt1[0] - pt2[0]; |
|
var deltaY = pt1[1] - pt2[1]; |
|
return Math.sqrt( deltaX * deltaX + deltaY * deltaY ); |
|
} |
|
|
|
/** |
|
* @param {Point} point The Point object to offset. |
|
* @param {number} dx The delta-x of the line segment from which `point` will |
|
* be offset. |
|
* @param {number} dy The delta-y of the line segment from which `point` will |
|
* be offset. |
|
* @param {number} distRatio The quotient of the distance to offset `point` |
|
* by and the distance of the line segment from which it is being offset. |
|
*/ |
|
function offsetPoint( point, dx, dy, distRatio ){ |
|
return [ |
|
point[ 0 ] - dy * distRatio, |
|
point[ 1 ] + dx * distRatio |
|
]; |
|
} |
|
|
|
/** |
|
* @param {array of Point} ctrlPoints The vertices of the (multi-segment) line |
|
* to be interpolate along. |
|
* @param {int} number The number of points to interpolate along the line; this |
|
* includes the endpoints, and has an effective minimum value of 2 (if a |
|
* smaller number is given, then the endpoints will still be returned). |
|
* @param {number} [offsetDist] An optional perpendicular distance to offset |
|
* each point from the line-segment it would otherwise lie on. |
|
* @param {int} [minGap] An optional minimum gap to maintain between subsequent |
|
* interpolated points; if the projected gap between subsequent points for |
|
* a set of `number` points is lower than this value, `number` will be |
|
* decreased to a suitable value. |
|
*/ |
|
function interpolateLineRange( ctrlPoints, number, offsetDist, minGap ){ |
|
minGap = minGap || 0; |
|
offsetDist = offsetDist || 0; |
|
|
|
// Calculate path distance from each control point (vertex) to the beginning |
|
// of the line, and also the ratio of `offsetDist` to the length of every |
|
// line segment, for use in computing offsets. |
|
var totalDist = 0; |
|
var ctrlPtDists = [ 0 ]; |
|
var ptOffsetRatios = []; |
|
for( var pt = 1; pt < ctrlPoints.length; pt++ ){ |
|
var dist = distance( ctrlPoints[ pt ], ctrlPoints[ pt - 1 ] ); |
|
totalDist += dist; |
|
ptOffsetRatios.push( offsetDist / dist ); |
|
ctrlPtDists.push( totalDist ); |
|
} |
|
|
|
if( totalDist / (number - 1) < minGap ){ |
|
number = totalDist / minGap + 1; |
|
} |
|
|
|
// Variables used to control interpolation. |
|
var step = totalDist / (number - 1); |
|
var interpPoints = [ offsetPoint( |
|
ctrlPoints[ 0 ], |
|
ctrlPoints[ 1 ][ 0 ] - ctrlPoints[ 0 ][ 0 ], |
|
ctrlPoints[ 1 ][ 1 ] - ctrlPoints[ 0 ][ 1 ], |
|
ptOffsetRatios[ 0 ] |
|
)]; |
|
var prevCtrlPtInd = 0; |
|
var currDist = 0; |
|
var currPoint = ctrlPoints[ 0 ]; |
|
var nextDist = step; |
|
|
|
for( pt = 0; pt < number - 2; pt++ ){ |
|
// Find the segment in which the next interpolated point lies. |
|
while( nextDist > ctrlPtDists[ prevCtrlPtInd + 1 ] ){ |
|
prevCtrlPtInd++; |
|
currDist = ctrlPtDists[ prevCtrlPtInd ]; |
|
currPoint = ctrlPoints[ prevCtrlPtInd ]; |
|
} |
|
|
|
// Interpolate the coordinates of the next point along the current segment. |
|
var remainingDist = nextDist - currDist; |
|
var ctrlPtsDeltaX = ctrlPoints[ prevCtrlPtInd + 1 ][ 0 ] - |
|
ctrlPoints[ prevCtrlPtInd ][ 0 ]; |
|
var ctrlPtsDeltaY = ctrlPoints[ prevCtrlPtInd + 1 ][ 1 ] - |
|
ctrlPoints[ prevCtrlPtInd ][ 1 ]; |
|
var ctrlPtsDist = ctrlPtDists[ prevCtrlPtInd + 1 ] - |
|
ctrlPtDists[ prevCtrlPtInd ]; |
|
var distRatio = remainingDist / ctrlPtsDist; |
|
|
|
currPoint = [ |
|
currPoint[ 0 ] + ctrlPtsDeltaX * distRatio, |
|
currPoint[ 1 ] + ctrlPtsDeltaY * distRatio |
|
]; |
|
|
|
// Offset currPoint according to `offsetDist`. |
|
var offsetRatio = offsetDist / ctrlPtsDist; |
|
interpPoints.push( offsetPoint( |
|
currPoint, ctrlPtsDeltaX, ctrlPtsDeltaY, ptOffsetRatios[ prevCtrlPtInd ]) |
|
); |
|
|
|
currDist = nextDist; |
|
nextDist += step; |
|
} |
|
|
|
interpPoints.push( offsetPoint( |
|
ctrlPoints[ ctrlPoints.length - 1 ], |
|
ctrlPoints[ ctrlPoints.length - 1 ][ 0 ] - |
|
ctrlPoints[ ctrlPoints.length - 2 ][ 0 ], |
|
ctrlPoints[ ctrlPoints.length - 1 ][ 1 ] - |
|
ctrlPoints[ ctrlPoints.length - 2 ][ 1 ], |
|
ptOffsetRatios[ ptOffsetRatios.length - 1 ] |
|
)); |
|
return interpPoints; |
|
} |
|
|
|
mapboxgl.accessToken = 'pk.eyJ1IjoidHdlbGNoIiwiYSI6Il9pX3dtb3cifQ.YcYnsO0X2p3x0HpHPFfleg'; |
|
var map = new mapboxgl.Map({ |
|
container: 'map', |
|
style: 'mapbox://styles/mapbox/streets-v8', |
|
center: [-122.580067, 45.548602], |
|
maxzoom: 20, |
|
zoom: 20 |
|
}); |
|
|
|
var iPathCoords = [ |
|
[ -122.579915799999981, 45.5485545, 0.0 ], [ -122.57994530000002, 45.5485536, 0.0 ], [ -122.579944, 45.5485996, 0.0 ], [ -122.580011, 45.54860810000001, 0.0 ], [ -122.5799507, 45.5486043, 0.0 ], [ -122.579954700000016, 45.5485489, 0.0 ], [ -122.579903799999983, 45.5485489, 0.0 ] |
|
]; |
|
var iPathLine = turf.linestring(iPathCoords); |
|
|
|
var iCoords = interpolateLineRange(iPathCoords, 100, .0000000001); |
|
var iPoints = []; |
|
for (var i=0; i<iCoords.length; i++) { |
|
iPoints.push(turf.point(iCoords[i])); |
|
} |
|
var iPointColl = turf.featurecollection(iPoints); |
|
|
|
map.on('style.load', function () { |
|
map.addSource("path", { |
|
"type": "geojson", |
|
"data": iPathLine, |
|
"maxzoom": 20 |
|
}); |
|
|
|
map.addLayer({ |
|
"id": "path", |
|
"type": "line", |
|
"source": "path", |
|
"layout": { |
|
"line-join": "round", |
|
"line-cap": "round" |
|
}, |
|
"paint": { |
|
"line-color": "#888", |
|
"line-width": 2 |
|
} |
|
}); |
|
|
|
map.addSource("peep", { |
|
"type": "geojson", |
|
"data": iPointColl, |
|
"maxzoom": 20 |
|
}); |
|
|
|
map.addLayer({ |
|
"id": "peep", |
|
"type": "circle", |
|
"source": "peep", |
|
"layout": {}, |
|
"paint": { |
|
"circle-radius": 3, |
|
"circle-opacity": 0.2 |
|
} |
|
}); |
|
|
|
}); |
|
</script> |
|
|
|
</body> |
|
</html> |