Skip to content

Commit

Permalink
#23: Drop layers
Browse files Browse the repository at this point in the history
  • Loading branch information
trasch committed Aug 30, 2024
1 parent f264491 commit fb25f81
Show file tree
Hide file tree
Showing 11 changed files with 128 additions and 43 deletions.
28 changes: 22 additions & 6 deletions Sources/MVTCLI/Dump.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,14 @@ extension CLI {

@Option(
name: .shortAndLong,
help: "Dump only the specified layer (can be repeated).")
help: "Dump the specified layer (can be repeated).")
var layer: [String] = []

@Option(
name: .shortAndLong,
help: "Drop the specified layer (can be repeated).")
var dropLayer: [String] = []

@Option(
name: [.customShort("P"), .long],
help: "Feature property to use for the layer name in input and output GeoJSONs. Needed for filtering by layer.")
Expand Down Expand Up @@ -46,8 +51,9 @@ extension CLI {
var path: String

mutating func run() async throws {
let layerAllowlist = layer.nonempty
let url = try options.parseUrl(fromPath: path)
let layerAllowlist = layer.asSet.subtracting(dropLayer).asArray.nonempty
let layerDenylist = dropLayer.asSet.subtracting(layer).asArray.nonempty

var tile = VectorTile(
contentsOfGeoJson: url,
Expand All @@ -73,7 +79,7 @@ extension CLI {
disableInputLayerProperty
{
if let layerAllowlist,
!layerAllowlist.isEmpty
layerAllowlist.isNotEmpty
{
if options.verbose {
print("Warning: GeoJSON without layers, no filtering possible")
Expand Down Expand Up @@ -105,10 +111,14 @@ extension CLI {
}

if tile.origin == .mvt
|| !disableInputLayerProperty,
let layerAllowlist
|| !disableInputLayerProperty
{
print("Layers: '\(layerAllowlist.joined(separator: ","))'")
if let layerAllowlist {
print("Allowed layers: '\(layerAllowlist.sorted().joined(separator: ","))'")
}
if let layerDenylist {
print("Dropped layers: '\(layerDenylist.sorted().joined(separator: ","))'")
}
}

print("Output options:")
Expand All @@ -118,7 +128,13 @@ extension CLI {
print("GeoJSON:")
}

var layerNames: [String] = []
if let layerDenylist {
layerNames = tile.layerNames.asSet.subtracting(layerDenylist).asArray
}

guard let data = tile.toGeoJson(
layerNames: layerNames,
prettyPrinted: true,
layerProperty: disableOutputLayerProperty ? nil : propertyName,
options: exportOptions)
Expand Down
21 changes: 18 additions & 3 deletions Sources/MVTCLI/Export.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,14 @@ extension CLI {

@Option(
name: .shortAndLong,
help: "Export only the specified layer (can be repeated).")
help: "Export the specified layer (can be repeated).")
var layer: [String] = []

@Option(
name: .shortAndLong,
help: "Drop the specified layer (can be repeated).")
var dropLayer: [String] = []

@Option(
name: [.customShort("P"), .long],
help: "Feature property to use for the layer name in the output GeoJSON.")
Expand Down Expand Up @@ -64,8 +69,9 @@ extension CLI {
mutating func run() async throws {
let (x, y, z) = try xyzOptions.parseXYZ(fromPaths: [path])
let url = try options.parseUrl(fromPath: path)
let layerAllowlist = layer.nonempty
let outputUrl = URL(fileURLWithPath: outputFile)
let layerAllowlist = layer.asSet.subtracting(dropLayer).asArray.nonempty
let layerDenylist = dropLayer.asSet.subtracting(layer).asArray.nonempty

if (try? outputUrl.checkResourceIsReachable()) ?? false {
if forceOverwrite {
Expand Down Expand Up @@ -104,7 +110,10 @@ extension CLI {
}

if let layerAllowlist {
print("Layers: '\(layerAllowlist.joined(separator: ","))'")
print("Allowed layers: '\(layerAllowlist.sorted().joined(separator: ","))'")
}
if let layerDenylist {
print("Dropped layers: '\(layerDenylist.sorted().joined(separator: ","))'")
}

print("Output options:")
Expand All @@ -113,7 +122,13 @@ extension CLI {
print(" - Simplification: \(exportOptions.simplifyFeatures)")
}

var layerNames: [String] = []
if let layerDenylist {
layerNames = tile.layerNames.asSet.subtracting(layerDenylist).asArray
}

guard let data = tile.toGeoJson(
layerNames: layerNames,
prettyPrinted: prettyPrint,
layerProperty: disableOutputLayerProperty ? nil : propertyName,
options: exportOptions)
Expand Down
6 changes: 6 additions & 0 deletions Sources/MVTCLI/Extensions/ArrayExtensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ extension Array {
isEmpty ? nil : self
}

var isNotEmpty: Bool { !isEmpty }

func get(at index: Int) -> Element? {
guard index >= -count,
index < count
Expand All @@ -23,6 +25,10 @@ extension Array {

extension Array where Element: Hashable {

var asSet: Set<Element> {
Set(self)
}

var uniqued: Self {
Array(Set(self))
}
Expand Down
34 changes: 24 additions & 10 deletions Sources/MVTCLI/Import.swift
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,14 @@ extension CLI {

@Option(
name: .shortAndLong,
help: "Import only the specified layer (can be repeated). ")
help: "Import the specified layer (can be repeated). ")
var layer: [String] = []

@Option(
name: .shortAndLong,
help: "Drop the specified layer (can be repeated).")
var dropLayer: [String] = []

@Option(
name: [.customShort("L"), .long],
help: "Layer name in the vector tile for the imported GeoJSON. Can be used with 'property-name' as a fallback name.")
Expand Down Expand Up @@ -84,7 +89,8 @@ extension CLI {

mutating func run() async throws {
let (x, y, z) = try xyzOptions.parseXYZ(fromPaths: [outputFile] + other)
let layerAllowlist = layer.nonempty
let layerAllowlist = layer.asSet.subtracting(dropLayer).asArray.nonempty
let layerDenylist = dropLayer.asSet.subtracting(layer).asArray.nonempty

let outputUrl = URL(fileURLWithPath: outputFile)
if (try? outputUrl.checkResourceIsReachable()) ?? false {
Expand Down Expand Up @@ -137,10 +143,13 @@ extension CLI {
print("Fallback layer name: \(layerName)")
}

if !disableInputLayerProperty,
let layerAllowlist
{
print("Layers: '\(layerAllowlist.joined(separator: ","))'")
if !disableInputLayerProperty {
if let layerAllowlist {
print("Allowed layers: '\(layerAllowlist.sorted().joined(separator: ","))'")
}
if let layerDenylist {
print("Dropped layers: '\(layerDenylist.sorted().joined(separator: ","))'")
}
}
}

Expand All @@ -165,12 +174,17 @@ extension CLI {

print("- \(otherUrl.lastPathComponent) (geojson)")

if !disableInputLayerProperty,
let layerAllowlist
{
if !disableInputLayerProperty {
otherGeoJSON.filterFeatures { feature in
guard let layerName: String = feature.property(for: propertyName) else { return false }
return layerAllowlist.contains(layerName)

if let layerAllowlist, layerAllowlist.contains(layerName) {
return true
}
if let layerDenylist, layerDenylist.contains(layerName) {
return false
}
return true
}
}

Expand Down
26 changes: 21 additions & 5 deletions Sources/MVTCLI/Merge.swift
Original file line number Diff line number Diff line change
Expand Up @@ -64,9 +64,14 @@ extension CLI {

@Option(
name: .shortAndLong,
help: "Merge only the specified layers (can be repeated).")
help: "Merge the specified layers (can be repeated).")
var layer: [String] = []

@Option(
name: .shortAndLong,
help: "Drop the specified layer (can be repeated).")
var dropLayer: [String] = []

@Option(
name: [.customShort("P"), .long],
help: "Feature property to use for the layer name in input and output GeoJSONs. Needed for filtering by layer.")
Expand Down Expand Up @@ -99,7 +104,8 @@ extension CLI {
var other: [String] = []

mutating func run() async throws {
let layerAllowlist = layer.nonempty
let layerAllowlist = layer.asSet.subtracting(dropLayer).asArray.nonempty
let layerDenylist = dropLayer.asSet.subtracting(layer).asArray.nonempty

var outputUrl: URL?
if let outputFile {
Expand Down Expand Up @@ -206,10 +212,14 @@ extension CLI {
}

if tile.origin == .mvt
|| !disableInputLayerProperty,
let layerAllowlist
|| !disableInputLayerProperty
{
print("Layers: '\(layerAllowlist.joined(separator: ","))'")
if let layerAllowlist {
print("Allowed layers: '\(layerAllowlist.sorted().joined(separator: ","))'")
}
if let layerDenylist {
print("Dropped layers: '\(layerDenylist.sorted().joined(separator: ","))'")
}
}
}

Expand Down Expand Up @@ -249,6 +259,12 @@ extension CLI {
otherTile = other
}

if otherTile != nil, let layerDenylist {
for droppedLayer in layerDenylist {
otherTile?.removeLayer(droppedLayer)
}
}

guard let otherTile else { throw CLIError("Failed to parse the tile at '\(path)'") }

if outputFormatToUse == .auto {
Expand Down
26 changes: 21 additions & 5 deletions Sources/MVTCLI/Query.swift
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,14 @@ extension CLI {

@Option(
name: .shortAndLong,
help: "Search only in this layer (can be repeated).")
help: "Search in this layer (can be repeated).")
var layer: [String] = []

@Option(
name: .shortAndLong,
help: "Drop the specified layer (can be repeated).")
var dropLayer: [String] = []

@Option(
name: [.customShort("P"), .long],
help: "Feature property to use for the layer name in input and output GeoJSONs. Needed for filtering by layer.")
Expand Down Expand Up @@ -106,8 +111,9 @@ extension CLI {
}
}

let layerAllowlist = layer.nonempty
let url = try options.parseUrl(fromPath: path)
let layerAllowlist = layer.asSet.subtracting(dropLayer).asArray.nonempty
let layerDenylist = dropLayer.asSet.subtracting(layer).asArray.nonempty

var tile = VectorTile(
contentsOfGeoJson: url,
Expand All @@ -127,6 +133,12 @@ extension CLI {
logger: options.verbose ? CLI.logger : nil)
}

if tile != nil, let layerDenylist {
for droppedLayer in layerDenylist {
tile?.removeLayer(droppedLayer)
}
}

guard let tile else { throw CLIError("Failed to parse the resource at '\(path)'") }

if options.verbose {
Expand All @@ -147,10 +159,14 @@ extension CLI {
}

if tile.origin == .mvt
|| !disableInputLayerProperty,
let layerAllowlist
|| !disableInputLayerProperty
{
print("Layers: '\(layerAllowlist.joined(separator: ","))'")
if let layerAllowlist {
print("Allowed layers: '\(layerAllowlist.sorted().joined(separator: ","))'")
}
if let layerDenylist {
print("Dropped layers: '\(layerDenylist.sorted().joined(separator: ","))'")
}
}
}

Expand Down
12 changes: 6 additions & 6 deletions Sources/MVTTools/Coders/MVTDecoder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ enum MVTDecoder {
let boundingBoxes: [BoundingBox] = layerFeatures.compactMap({ $0.boundingBox })

var layerBoundingBox: BoundingBox?
if !boundingBoxes.isEmpty {
if boundingBoxes.isNotEmpty {
layerBoundingBox = boundingBoxes.reduce(boundingBoxes[0], +)
}

Expand Down Expand Up @@ -187,7 +187,7 @@ enum MVTDecoder {
ofType: featureType,
projectionFunction: projectionFunction)

guard !multiCoordinates.isEmpty else { return nil }
guard multiCoordinates.isNotEmpty else { return nil }

var feature: Feature?

Expand Down Expand Up @@ -228,14 +228,14 @@ enum MVTDecoder {
var currentRings: [Ring] = []

for ring in rings {
if ring.isUnprojectedClockwise, !currentRings.isEmpty {
if ring.isUnprojectedClockwise, currentRings.isNotEmpty {
polygons.append(ifNotNil: Polygon(currentRings.map({ $0.coordinates })))
currentRings = []
}
currentRings.append(ring)
}

if !currentRings.isEmpty {
if currentRings.isNotEmpty {
polygons.append(ifNotNil: Polygon(currentRings.map({ $0.coordinates })))
}

Expand Down Expand Up @@ -307,7 +307,7 @@ enum MVTDecoder {
y += MVTDecoder.zigZagDecode(Int(dy))

if commandId == MVTDecoder.commandIdMoveTo,
!coordinates.isEmpty
coordinates.isNotEmpty
{
result.append(coordinates)
coordinates = []
Expand All @@ -318,7 +318,7 @@ enum MVTDecoder {
}
}

if !coordinates.isEmpty {
if coordinates.isNotEmpty {
result.append(coordinates)
}

Expand Down
4 changes: 3 additions & 1 deletion Sources/MVTTools/Extensions/ArrayExtensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import Foundation

extension Array {

var isNotEmpty: Bool { !isEmpty }

/// Adds a new element at the end of the array if it's not *nil*.
mutating func append(ifNotNil element: Element?) {
guard let element else { return }
Expand All @@ -13,7 +15,7 @@ extension Array {

/// Returns the array's elements pairwise. For arrays with uneven length, the last element will be skipped.
func pairs() -> [(first: Element, second: Element)] {
guard !isEmpty else { return [] }
guard isNotEmpty else { return [] }

return (0 ..< (count / 2)).compactMap { (index) in
let i = index * 2
Expand Down
Loading

0 comments on commit fb25f81

Please sign in to comment.