Last active
December 9, 2022 18:06
-
-
Save nixta/d8b61aac11c001075c8e7acc84704415 to your computer and use it in GitHub Desktop.
Read polyline from ArcGIS Route Service compressed geometry.
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
extension AGSGeometry { | |
static func decodeCompressedGeometry(_ compressedString: String, spatialReference sr: AGSSpatialReference) -> AGSPolyline? { | |
// Break the string up into signed parts. | |
// The first part is a coefficient. | |
// Subsequent pairs of parts make up the remaining coordinates, encoded. | |
let pattern = #"((\+|\-)[^\+\-]+)"# | |
guard let regex = try? NSRegularExpression(pattern: pattern, options: []) else { | |
return nil | |
} | |
let compressedParts = regex.matches( | |
in: compressedString, | |
range: NSRange(compressedString.startIndex ..< compressedString.endIndex, | |
in: compressedString) | |
).lazy.compactMap { | |
Range($0.range(at: 0), in: compressedString) | |
}.compactMap { range -> Double? in | |
Double(Int(compressedString[range], radix: 32) ?? 0) | |
} | |
guard let coefficient = compressedParts.first else { | |
preconditionFailure("Invalid compressed geometry. Needs coefficient.") | |
} | |
guard compressedParts.count >= 5, | |
(compressedParts.count - 1) % 2 == 0 else { | |
preconditionFailure("Invalid compressed geometry. Needs even number of subsequent parts, and at least 2 subsequent parts!") | |
} | |
struct XYVector { | |
var x: Double | |
var y: Double | |
mutating func applyDelta(_ delta: XYVector) { | |
x += delta.x | |
y += delta.y | |
} | |
} | |
let deltas = compressedParts.dropFirst().reduce([[Double]]()) { partialResult, nextVal in | |
if let lastCoordinate = partialResult.last, | |
lastCoordinate.count < 2 { | |
return partialResult.dropLast() + [lastCoordinate + [nextVal]] | |
} else { | |
return partialResult + [[nextVal]] | |
} | |
}.map( { XYVector(x: $0[0], y: $0[1]) } ) | |
var cumulativeCoordinate = XYVector(x: 0, y: 0) | |
let builder = deltas.reduce(AGSPolylineBuilder(spatialReference: sr)) { builder, delta in | |
// Each coordinate pair is a delta from the last point, so we just keep a cumulative last coordinate. | |
cumulativeCoordinate.applyDelta(delta) | |
builder.add(AGSPoint(x: cumulativeCoordinate.x / coefficient, | |
y: cumulativeCoordinate.y / coefficient, | |
spatialReference: sr)) | |
return builder | |
} | |
return builder.toGeometry() | |
} | |
} | |
// See https://github.com/Esri/terraformer-arcgis-parser/pull/15/files for algorithm. | |
// and https://help.arcgis.com/en/sdk/10.0/java_ao_adf/api/arcobjects/com/esri/arcgis/networkanalyst/INACompactStreetDirectionProxy.html#getCompressedGeometry() |
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
// Example | |
var sampleCompressedGeometry = "+1m91-66os4+1poms+1+91+3+3j" | |
let line = AGSGeometry.decodeCompressedGeometry(sampleCompressedGeometry, spatialReference: .wgs84()) | |
print("Got a line: \(line)") | |
var decodedCoordinates = [ | |
[ -117.1816137447153, 34.057461545380946 ],[ -117.18159575425025, 34.06266078978142 ], [ -117.18154178285509, 34.06472969326257 ] | |
].map { AGSPointMakeWGS84($0[1], $0[0]) } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment