Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
splitting polyline paths across the international dateline in leaflet (sorry that it uses a mix of jQuery and lo-dash for helpers and is in coffeescript)
Array.prototype.splitAtEvery = (callback) ->
sections = []
arrayClone = this.slice(0)
$.each(arrayClone, (idx, item) =>
sectionsLength = 0
_.each(sections, (section) =>
sectionsLength += section.length;
)
if callback(this, idx, item) == true
sections.push(arrayClone.slice(0, idx+1-sectionsLength))
arrayClone = arrayClone.slice(idx+1-sectionsLength, arrayClone.length)
return
)
sections.push(arrayClone)
return sections
drawPolylines = (latLons, leafletMap) ->
normalizeIndexes = []
$.each(latLons, (i, latLon) =>
last = latLons[i-1]
if last?
if Math.abs(latLon.lng - last.lng) > 180
normalizeIndexes.push(i)
)
normalizeIndexesRef = normalizeIndexes.slice(0)
$.each(normalizeIndexes, (i, normalizeIndex) =>
flipDirection = if latLons[normalizeIndexesRef[i]].lng < 0 then -1 else 1
latLons.splice(normalizeIndexesRef[i], 0, L.latLng(latLons[normalizeIndexesRef[i]].lat, flipDirection * -179.99999))
latLons.splice(normalizeIndexesRef[i]+1, 0, L.latLng(latLons[normalizeIndexesRef[i]].lat, flipDirection * 179.99999))
normalizeIndexesRef = _.map(normalizeIndexesRef, (idx) => idx+2)
)
sections = latLons.splitAtEvery((array, i, val) ->
return (Math.abs(val.lng) == 179.99999) && \
(Math.abs(array[i+1].lng) == 179.99999)
)
$.each(sections, (i, section) ->
polyline = L.polyline(section, {color:'#0099cc'}).addTo(leafletMap);
)
return latLons
@mikeatlas

This comment has been minimized.

Copy link
Owner Author

mikeatlas commented Oct 24, 2014

See this in action before/after for a zig-zag path down the date line:

screen shot 2014-10-24 at 11 13 54 am

screen shot 2014-10-24 at 5 44 53 pm

Credit to the idea for the algorithm from http://gis.stackexchange.com/questions/18562/how-can-i-make-a-polyline-wrap-around-the-world

@mikeatlas

This comment has been minimized.

Copy link
Owner Author

mikeatlas commented May 23, 2015

The gist ... of the gist ... is that it detects polylines which cross the IDL by comparing serial pairs of points in the polyline, and then splits the polyline into two or more logical polylines if any of the point-pairs cross the IDL. This introduces some distortions as I basically am chopping or rounding at approximate mid-points in polylines (to .99999 decimal), while effectively adding point data into the logical polyline that didn't actually exist in the original set, which are then artifacts strictly for rendering purposes around leaflet's limitations. Kinda lame but at least it "looks" pretty-right. I also didn't experiment with multi-circumnavigation polylines. I think it might work, though. Also note that the world-copy-jump feature thing makes this even less intuitive and complicated to resolve. Not surprising there's no official support for it in Leaflet yet, but also still disappointing too.

@KevinCarhart

This comment has been minimized.

Copy link

KevinCarhart commented May 23, 2015

Thanks for making this!

@mikeatlas

This comment has been minimized.

Copy link
Owner Author

mikeatlas commented May 26, 2015

YW. @KevinCarhart, here's a quick whiteboard of the algorithm: idl split

@markpolarisdesign

This comment has been minimized.

Copy link

markpolarisdesign commented Jul 22, 2016

I have problem " normalizeIndexesRef = _.map(normalizeIndexesRef, (idx) => idx+2) "
main.js:391 Uncaught ReferenceError: _ is not defined

@dwy904

This comment has been minimized.

Copy link

dwy904 commented May 10, 2017

Does it work now ?

@tripler

This comment has been minimized.

Copy link

tripler commented Aug 30, 2017

The whiteboard is a bit misleading as the gist does not attempt to calculate the correct latitude at the antimeridian intersection, which leads to those unusual horizontal lines and incorrect slopes in the action shot. Adding it in is pretty simple though. Just calculate the latitude in the step before the splice:

latLons.splice(normalizeIndexesRef[i], 0, L.latLng(latLons[normalizeIndexesRef[i]].lat, flipDirection * -179.99999))

I opted to use the simple linear equation of y = mx+b to determine the latitude (y) at the antimeridian (x=180) using the slope (points i and i-1), though the SE link mentioned above goes into a more accurate spherical determination.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.