Skip to content

Commit

Permalink
Some cleanup
Browse files Browse the repository at this point in the history
  • Loading branch information
trasch committed Aug 20, 2024
1 parent 421a01d commit 4ebcabb
Show file tree
Hide file tree
Showing 8 changed files with 60 additions and 33 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ You can install the command line tool `mvt` either

`mvt` works with vector tiles or GeoJSON files from local disk or served from a web server.

Layers in GeoJSON files (containing a FeatureCollection) can be represented by adding a property `vt_tile` to each Feature.
Layers in GeoJSON files (containing a FeatureCollection) can be represented by adding a property `vt_tile` to each Feature (can be overriden with a command line option).
`mvt` will add this property to all created GeoJSONs.

```bash
Expand Down
6 changes: 3 additions & 3 deletions Sources/MVTCLI/Dump.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,12 @@ extension CLI {

@Option(
name: [.customShort("P"), .long],
help: "Feature property to use for the layer name in input and output GeoJSONs.")
help: "Feature property to use for the layer name in input and output GeoJSONs. Needed for filtering by layer.")
var propertyName: String = VectorTile.defaultLayerPropertyName

@Flag(
name: [.customLong("Di", withSingleDash: true), .long],
help: "Don't parse the layer name (option 'property-name') from Feature properties in the input GeoJSONs. Might speed up GeoJSON parsing considerably. Needed for filtering by layer.")
help: "Don't parse the layer name (option 'property-name') from Feature properties in the input GeoJSONs. Might speed up GeoJSON parsing considerably.")
var disableInputLayerProperty: Bool = false

@Flag(
Expand All @@ -36,7 +36,7 @@ extension CLI {
var options: Options

@Argument(
help: "The vector tile or GeoJSON (file or URL)",
help: "The vector tile or GeoJSON (file or URL).",
completion: .file(extensions: ["pbf", "mvt", "geojson", "json"]))
var path: String

Expand Down
22 changes: 12 additions & 10 deletions Sources/MVTCLI/Export.swift
Original file line number Diff line number Diff line change
Expand Up @@ -47,24 +47,15 @@ extension CLI {
var options: Options

@Argument(
help: "The vector tile (file or URL)",
help: "The vector tile (file or URL).",
completion: .file(extensions: ["pbf", "mvt"]))
var path: String

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)
if (try? outputUrl.checkResourceIsReachable()) ?? false {
if forceOverwrite {
print("Existing file '\(outputUrl.lastPathComponent)' will be overwritten")
}
else {
throw CLIError("Output file must not exist (use --force-overwrite to overwrite existing files)")
}
}

if options.verbose {
print("Dumping tile '\(url.lastPathComponent)' [\(x),\(y)]@\(z) to '\(outputUrl.lastPathComponent)'")
Expand All @@ -79,6 +70,17 @@ extension CLI {
}
}

if (try? outputUrl.checkResourceIsReachable()) ?? false {
if forceOverwrite {
if options.verbose {
print("Existing file '\(outputUrl.lastPathComponent)' will be overwritten")
}
}
else {
throw CLIError("Output file must not exist (use --force-overwrite to overwrite existing files)")
}
}

guard let tile = VectorTile(
contentsOf: url,
x: x,
Expand Down
29 changes: 24 additions & 5 deletions Sources/MVTCLI/Import.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,19 @@ extension CLI {
help: "Append to an existing 'output' file.")
var append = false

@Option(
name: .shortAndLong,
help: "Import only the specified layer (can be repeated). ")
var layer: [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.")
var layerName: String?

@Option(
name: [.customShort("P"), .long],
help: "Feature property to use for the layer name in input GeoJSONs. Fallback to 'layer-name' or a default if the property is not present.")
help: "Feature property to use for the layer name in input GeoJSONs. Fallback to 'layer-name' or a default if the property is not present. Needed for filtering by layer.")
var propertyName: String = VectorTile.defaultLayerPropertyName

@Flag(
Expand All @@ -48,20 +53,25 @@ extension CLI {
var options: Options

@Argument(
help: "GeoJSON resources to import (file or URL)",
help: "GeoJSON resources to import (file or URL).",
completion: .file(extensions: ["json", "geojson"]))
var other: [String] = []

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

let outputUrl = URL(fileURLWithPath: outputFile)
if (try? outputUrl.checkResourceIsReachable()) ?? false {
if forceOverwrite {
print("Existing file '\(outputUrl.lastPathComponent)' will be overwritten")
if options.verbose {
print("Existing file '\(outputUrl.lastPathComponent)' will be overwritten")
}
}
else if append {
print("Existing file '\(outputUrl.lastPathComponent)' will be appended")
if options.verbose {
print("Existing file '\(outputUrl.lastPathComponent)' will be appended")
}
}
else {
throw CLIError("Output file must not exist (use --force-overwrite or --append to overwrite existing files)")
Expand Down Expand Up @@ -118,12 +128,21 @@ extension CLI {
}
}

guard let otherGeoJSON = FeatureCollection(contentsOf: otherUrl) else {
guard var otherGeoJSON = FeatureCollection(contentsOf: otherUrl) else {
throw CLIError("Failed to parse the GeoJSON at '\(path)'")
}

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

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

tile.addGeoJson(
geoJson: otherGeoJSON,
layerName: layerName,
Expand Down
2 changes: 1 addition & 1 deletion Sources/MVTCLI/Info.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ extension CLI {
var options: Options

@Argument(
help: "The vector tile or GeoJSON (file or URL)",
help: "The vector tile or GeoJSON (file or URL).",
completion: .file(extensions: ["pbf", "mvt", "geojson", "json"]))
var path: String

Expand Down
16 changes: 10 additions & 6 deletions Sources/MVTCLI/Merge.swift
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,12 @@ extension CLI {

@Option(
name: [.customShort("P"), .long],
help: "Feature property to use for the layer name in input and output GeoJSONs.")
help: "Feature property to use for the layer name in input and output GeoJSONs. Needed for filtering by layer.")
var propertyName: String = VectorTile.defaultLayerPropertyName

@Flag(
name: [.customLong("Di", withSingleDash: true), .long],
help: "Don't parse the layer name (option 'property-name') from Feature properties in the input GeoJSONs. Might speed up GeoJSON parsing considerably. Needed for filtering by layer.")
help: "Don't parse the layer name (option 'property-name') from Feature properties in the input GeoJSONs. Might speed up GeoJSON parsing considerably.")
var disableInputLayerProperty: Bool = false

@Flag(
Expand All @@ -69,7 +69,7 @@ extension CLI {
var options: Options

@Argument(
help: "Vector tiles or GeoJSONs to merge (file or URL)",
help: "Vector tiles or GeoJSONs to merge (file or URL).",
completion: .file(extensions: ["pbf", "mvt", "json", "geojson"]))
var other: [String] = []

Expand All @@ -81,10 +81,14 @@ extension CLI {
outputUrl = URL(fileURLWithPath: outputFile)
if let outputUrl, (try? outputUrl.checkResourceIsReachable()) ?? false {
if forceOverwrite {
print("Existing file '\(outputUrl.lastPathComponent)' will be overwritten")
if options.verbose {
print("Existing file '\(outputUrl.lastPathComponent)' will be overwritten")
}
}
else if append {
print("Existing file '\(outputUrl.lastPathComponent)' will be appended")
if options.verbose {
print("Existing file '\(outputUrl.lastPathComponent)' will be appended")
}
}
else {
throw CLIError("Output file must not exist (use --force-overwrite or --append to overwrite existing files)")
Expand Down Expand Up @@ -230,7 +234,7 @@ extension CLI {
if outputFormatToUse == .auto {
switch otherTile.origin {
case .geoJson: outputFormatToUse = .geojson
default: outputFormatToUse = .mvt
case .mvt, .none: outputFormatToUse = .mvt
}
}

Expand Down
12 changes: 7 additions & 5 deletions Sources/MVTCLI/Query.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,12 @@ extension CLI {

@Option(
name: [.customShort("P"), .long],
help: "Feature property to use for the layer name in input and output GeoJSONs.")
help: "Feature property to use for the layer name in input and output GeoJSONs. Needed for filtering by layer.")
var propertyName: String = VectorTile.defaultLayerPropertyName

@Flag(
name: [.customLong("Di", withSingleDash: true), .long],
help: "Don't parse the layer name (option 'property-name') from Feature properties in the input GeoJSONs. Might speed up GeoJSON parsing considerably. Needed for filtering by layer.")
help: "Don't parse the layer name (option 'property-name') from Feature properties in the input GeoJSONs. Might speed up GeoJSON parsing considerably.")
var disableInputLayerProperty: Bool = false

@Flag(
Expand All @@ -56,19 +56,21 @@ extension CLI {
var options: Options

@Argument(
help: "The vector tile or GeoJSON (file or URL)",
help: "The vector tile or GeoJSON (file or URL).",
completion: .file(extensions: ["pbf", "mvt", "geojson", "json"]))
var path: String

@Argument(help: "Search term, can be a string or a coordinate in the form 'latitude,longitude,tolerance(meters)'")
@Argument(help: "Search term, can be a string or a coordinate in the form 'latitude,longitude,tolerance(meters)'.")
var searchTerm: String

mutating func run() async throws {
if let outputFile {
let outputUrl = URL(fileURLWithPath: outputFile)
if (try? outputUrl.checkResourceIsReachable()) ?? false {
if forceOverwrite {
print("Existing file '\(outputUrl.lastPathComponent)' will be overwritten")
if options.verbose {
print("Existing file '\(outputUrl.lastPathComponent)' will be overwritten")
}
}
else {
throw CLIError("Output file must not exist (use --force-overwrite to overwrite existing files)")
Expand Down
4 changes: 2 additions & 2 deletions Tests/MVTToolsTests/GeoJsonTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,13 @@ final class GeoJsonTests: XCTestCase {
let tile = try XCTUnwrap(VectorTile(data: mvt, x: 8716, y: 8015, z: 14))

// Export all layers
let allLayersFc = try XCTUnwrap(FeatureCollection(jsonData: try XCTUnwrap(tile.toGeoJson())))
let allLayersFc = try XCTUnwrap(FeatureCollection(jsonData: try XCTUnwrap(tile.toGeoJson(layerProperty: VectorTile.defaultLayerPropertyName))))
let allLayersLayerList = Set(try XCTUnwrap(allLayersFc.features.compactMap({ $0.properties[VectorTile.defaultLayerPropertyName] as? String })))
XCTAssertEqual(Set(tile.layersWithContent.map(\.0)), allLayersLayerList)

// Export some layers
let someLayers = ["landuse", "waterway", "water"]
let someLayersFc = try XCTUnwrap(FeatureCollection(jsonData: try XCTUnwrap(tile.toGeoJson(layerNames: someLayers, additionalFeatureProperties: ["test": "test"]))))
let someLayersFc = try XCTUnwrap(FeatureCollection(jsonData: try XCTUnwrap(tile.toGeoJson(layerNames: someLayers, additionalFeatureProperties: ["test": "test"], layerProperty: VectorTile.defaultLayerPropertyName))))
let someLayersLayerList = Set(try XCTUnwrap(someLayersFc.features.compactMap({ $0.properties[VectorTile.defaultLayerPropertyName] as? String })))
XCTAssertEqual(Set(someLayers), someLayersLayerList)
XCTAssertTrue(someLayersFc.features.allSatisfy({ ($0.properties["test"] as? String) == "test" }))
Expand Down

0 comments on commit 4ebcabb

Please sign in to comment.