Skip to content

Instantly share code, notes, and snippets.

@gmaclennan
Last active January 21, 2017 20:50
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gmaclennan/413669ae56a397822f3e4afb4de68e3b to your computer and use it in GitHub Desktop.
Save gmaclennan/413669ae56a397822f3e4afb4de68e3b to your computer and use it in GitHub Desktop.
Dissolve connected GeoJSON LineStrings
Display the source blob
Display the rendered blob
Raw
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": {
},
"geometry": {
"type": "LineString",
"coordinates": [
[
-0.835,
-0.211
],
[
-0.73,
-0.116
],
[
-0.665,
-0.044
],
[
-0.575,
0.005
]
]
}
},
{
"type": "Feature",
"properties": {
},
"geometry": {
"type": "LineString",
"coordinates": [
[
-0.575,
0.005
],
[
-0.516,
0.038
],
[
-0.431,
0.09
],
[
-0.344,
0.152
]
]
}
},
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "LineString",
"coordinates": [
[
-0.344,
0.152
],
[
-0.31,
0.231
],
[
-0.262,
0.352
],
[
-0.164,
0.435
]
]
}
},
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "LineString",
"coordinates": [
[
-0.344,
0.152
],
[
-0.22,
0.167
],
[
-0.105,
0.229
],
[
0.042,
0.273
]
]
}
}
]
}
var meta = require('@turf/meta')
var lineString = require('@turf/helpers').lineString
var roundTo = require('round-to')
module.exports = function dissolve (fc) {
var endPoints = {}
var index = []
var dissolvedFeatures = []
meta.featureEach(fc, indexPoints)
function indexPoints (f) {
if (f.geometry.type === 'MultiLineString') {
return f.geometry.coordinates.forEach(function (_) {
indexPoints(lineString(_, f.properties))
})
}
if (f.geometry.type !== 'LineString') return
index.push(f)
var id = index.length - 1
var coords = getCoords(f)
var p1 = getId(coords[0])
var p2 = getId(coords[coords.length - 1])
// Closed loops and single-point lines should not be dissolved
if (p1 === p2) return
if (!endPoints[p1]) endPoints[p1] = []
endPoints[p1].push(id)
if (!endPoints[p2]) endPoints[p2] = []
endPoints[p2].push(id)
}
Object.keys(endPoints).forEach(function (pointId) {
var ids = endPoints[pointId]
if (ids.length !== 2) return
var merged = merge(index[ids[0]], index[ids[1]])
if (!merged) return
index[ids[0]] = index[ids[1]] = merged
})
for (var i = 0; i < index.length; i++) {
if (dissolvedFeatures.indexOf(index[i]) > -1) continue
dissolvedFeatures.push(index[i])
}
return {
type: 'FeatureCollection',
features: dissolvedFeatures
}
}
function getId (coord, precision) {
if (!precision) return coord.join('/')
return roundTo(coord[0], precision) + '/' + roundTo(coord[1])
}
function getCoords (f) {
return f.geometry.coordinates
}
function merge (f1, f2) {
var coords1 = getCoords(f1)
var coords2 = getCoords(f2)
var coords
var s1 = coords1[0]
var e1 = coords1[coords1.length - 1]
var s2 = coords2[0]
var e2 = coords2[coords2.length - 1]
// TODO: Lines that both start or end at the same point are randomly
// reversed to merge, leaving no guarantee on final line direction
if (coordsEq(e1, s2)) {
coords = coords1.concat(coords2.slice(1))
} else if (coordsEq(e2, s1)) {
coords = coords2.concat(coords1.slice(1))
} else if (coordsEq(e1, e2)) {
coords = coords1.concat(coords2.slice(0, -1).reverse())
} else if (coordsEq(s1, s2)) {
coords = coords1.concat(coords2.slice(1).reverse())
} else {
return null
}
var mergedProps = {}
// If props are equal or undefined in one of the input features, then include in merged props
for (var prop in f1.properties) {
if (f2.properties.hasOwnProperty(prop) && f1.properties[prop] !== f2.properties[prop]) continue
mergedProps[prop] = f1.properties[prop]
}
for (prop in f2.properties) {
if (f1.properties.hasOwnProperty(prop)) continue
mergedProps[prop] = f2.properties[prop]
}
return lineString(coords, mergedProps)
}
// Needs to check precision
function coordsEq (c1, c2) {
return c1[0] === c2[0] && c1[1] === c2[1]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment