Skip to content

Commit

Permalink
#23: Drop layers (#30)
Browse files Browse the repository at this point in the history
  • Loading branch information
trasch authored Sep 2, 2024
1 parent f264491 commit 207cb09
Show file tree
Hide file tree
Showing 14 changed files with 143 additions and 49 deletions.
8 changes: 4 additions & 4 deletions Package.resolved
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-argument-parser",
"state" : {
"revision" : "0fbc8848e389af3bb55c182bc19ca9d5dc2f255b",
"version" : "1.4.0"
"revision" : "41982a3656a71c768319979febd796c6fd111d5c",
"version" : "1.5.0"
}
},
{
Expand All @@ -42,8 +42,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-protobuf",
"state" : {
"revision" : "9f0c76544701845ad98716f3f6a774a892152bcb",
"version" : "1.26.0"
"revision" : "edb6ed4919f7756157fe02f2552b7e3850a538e5",
"version" : "1.28.1"
}
}
],
Expand Down
4 changes: 2 additions & 2 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@ let package = Package(
dependencies: [
.package(url: "https://github.com/Outdooractive/gis-tools", from: "1.8.3"),
.package(url: "https://github.com/1024jp/GzipSwift.git", from: "5.2.0"),
.package(url: "https://github.com/apple/swift-argument-parser", from: "1.4.0"),
.package(url: "https://github.com/apple/swift-argument-parser", from: "1.5.0"),
.package(url: "https://github.com/apple/swift-log.git", from: "1.6.1"),
.package(url: "https://github.com/apple/swift-protobuf", from: "1.26.0"),
.package(url: "https://github.com/apple/swift-protobuf", from: "1.26.1"),
],
targets: [
.executableTarget(
Expand Down
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
9 changes: 9 additions & 0 deletions Sources/MVTCLI/Extensions/SetExtensions.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import Foundation

extension Set {

var asArray: [Element] {
Array(self)
}

}
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: "Do not search in 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
Loading

0 comments on commit 207cb09

Please sign in to comment.