class SegmentJoiner {
constructor(segments) {
if (!segments) throw Error("Missing required argument to parameter 'segments'")
if (!(segments instanceof Array)) throw("Argument to parameter 'segments' must be an array")
this.segments = segments;
this.segmentsCopy = [...segments];
this.orderedSegmentIndices = [];
this.segmentMap = this.groupBySegment(segments);
this.weldMap = this.groupByWelds(segments);
}
get getWeldMap() {
return this.copyWeldMap
}
get getSegmentMap() {
return this.segmentMap
}
groupBySegment(segments) {
const startEndPerSegment = new Map();
segments.map((coords, i) => {
const start = coords[0];
const { length } = coords;
const end = coords[length - 1];
startEndPerSegment.set(i, { start, end });
})
return startEndPerSegment;
}
groupByWelds(segments) {
const coordsMap = new Map();
// Direction of segments is not always the same
segments.map((coords, i) => {
const { length } = coords;
const startNode = coords[0];
const endNode = coords[length - 1];
// turn coords into a string so they're unique in the Map
const startNodeString = startNode.toString();
const endNodeString = endNode.toString();
if (coordsMap.has(startNodeString)) {
if (coordsMap.get(startNodeString).has("startOf")) {
coordsMap.get(startNodeString).set("alsoStartOf", i)
} else {
coordsMap.get(startNodeString).set("startOf", i)
}
} else {
coordsMap.set(startNodeString, new Map([["startOf", i]]))
}
if (coordsMap.has(endNodeString)) {
if (coordsMap.get(endNodeString).has("endOf")) {
coordsMap.get(endNodeString).set("alsoEndOf", i)
} else {
coordsMap.get(endNodeString).set("endOf", i)
}
} else {
coordsMap.set(endNodeString, new Map([["endOf", i]]))
}
})
return coordsMap
}
// 1. find weld with only start. Read start segment (or only end)
startSegment() {
for (const [coordString, startEndOfSegmentMap] of this.weldMap) {
if (startEndOfSegmentMap.size === 1) {
if (startEndOfSegmentMap.has("startOf")) {
console.log("start segment:", startEndOfSegmentMap.get("startOf"))
return { index: startEndOfSegmentMap.get("startOf"), reverse: false }
}
}
}
// only if there's none with only startOf in the weldMap
for (const [coordString, startEndOfSegmentMap] of this.weldMap) {
if (startEndOfSegmentMap.size === 1) {
if (startEndOfSegmentMap.has("endOf")) {
console.log("Reversed, MultiLine has no startOf at ends", startEndOfSegmentMap.get("endOf"))
return { index: startEndOfSegmentMap.get("endOf"), reverse: true }
}
}
}
}
findNextSegment(weld, prevSegment) {
if (!weld || !prevSegment) {
return "Not enough args to findNextSegment :("
}
console.log("finding next segment of weld", {weld}, "with prev segment", prevSegment)
const segmentsOfWeld = this.weldMap.get(weld);
// TODO: could pass in the previous end type and delete that from the map with needing to iterate over it
for (const [termination, segmentIndex] of segmentsOfWeld) {
console.log(termination, prevSegment.index)
if (segmentIndex === prevSegment.index) {
console.log("Remove previous segment index", segmentIndex)
segmentsOfWeld.delete(termination)
}
}
if (segmentsOfWeld.has('startOf')) {
console.log("Normal direction")
const index = segmentsOfWeld.get("startOf")
segmentsOfWeld.delete("startOf")
return { index, reverse: false }
}
if (segmentsOfWeld.has('endOf')) {
console.log("Reverse direction")
const index = segmentsOfWeld.get("endOf");
segmentsOfWeld.delete("endOf")
return { index, reverse: true }
}
if (segmentsOfWeld.has('alsoStartOf')) {
console.log("Again normal direction")
const index = segmentsOfWeld.get("alsoStartOf");
segmentsOfWeld.delete("alsoStartOf")
return { index, reverse: false }
}
if (segmentsOfWeld.has('alsoEndOf')) {
console.log("Again reverse direction")
const index = segmentsOfWeld.get("alsoEndOf");
segmentsOfWeld.delete("alsoEndOf")
return { index, reverse: true }
}
// this must be the end (or some condition I did not think of ¯\_(ツ)_/¯)
return false;
}
findNextWeld(segment) {
console.log("finding next weld of segment", segment)
const nextWeld = this.segmentMap.get(segment.index)
// special case for the first node?
if (!segment.reverse) return nextWeld.end.toString()
return nextWeld.start.toString() // TODO: or alsoStartOf
}
// TODO: turn this into a recursive generator
// recursive function
joinSegments(currentWeld, currentSegmentIndex) {
let nextSegment = this.findNextSegment(currentWeld, currentSegmentIndex)
if (nextSegment === false) return `${currentSegmentIndex} is is the final segmentIndex of this LineString`
this.orderedSegmentIndices.push(nextSegment)
let nextWeld = this.findNextWeld(nextSegment)
// yield*
this.joinSegments(nextWeld, nextSegment)
}
joinAllSegments() {
this.orderedSegmentIndices = [];
const firstSegment = this.startSegment();
const firstWeld = this.findNextWeld(firstSegment);
this.orderedSegmentIndices.push(firstSegment);
this.joinSegments(firstWeld, firstSegment);
return this.mergeMultiLineString();
}
mergeMultiLineString() {
if (!this.orderedSegmentIndices.length) return "No segment indices found"
const orderedLineString = this.orderedSegmentIndices.map((seg) => {
if (seg.reverse) return this.segments[seg.index].reverse(); // mutates this.segments[seg]
return this.segments[seg.index];
})
return orderedLineString.flat(); // remove double nodes too?
}
}