Skip to content

Commit

Permalink
#26: Output export options (#29)
Browse files Browse the repository at this point in the history
  • Loading branch information
trasch authored Aug 27, 2024
1 parent d365e75 commit f264491
Show file tree
Hide file tree
Showing 9 changed files with 364 additions and 66 deletions.
27 changes: 22 additions & 5 deletions Sources/MVTCLI/Dump.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,17 @@ extension CLI {
@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.")
var disableInputLayerProperty: Bool = false
var disableInputLayerProperty = false

@Flag(
name: [.customLong("Do", withSingleDash: true), .long],
help: "Don't add the layer name (option 'property-name') as a Feature property in the output GeoJSONs.")
var disableOutputLayerProperty: Bool = false
var disableOutputLayerProperty = false

@Option(
name: [.customLong("oSm", withSingleDash: true), .long],
help: "Simplify output features using meters.")
var simplifyMeters: Int?

@OptionGroup
var xyzOptions: XYZOptions
Expand Down Expand Up @@ -77,10 +82,15 @@ extension CLI {
}
}

var exportOptions = VectorTile.ExportOptions()
if let simplifyMeters, simplifyMeters > 0 {
exportOptions.simplifyFeatures = .meters(Double(simplifyMeters))
}

if options.verbose {
print("Dumping \(tile.origin) tile '\(url.lastPathComponent)' [\(tile.x),\(tile.y)]@\(tile.z)")
print("Property name: \(propertyName)")

print("Layer property name: \(propertyName)")
if disableInputLayerProperty {
print(" - disable input layer property")
}
Expand All @@ -96,15 +106,22 @@ extension CLI {

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

print("Output options:")
print(" - Pretty print: true")
print(" - Simplification: \(exportOptions.simplifyFeatures)")

print("GeoJSON:")
}

guard let data = tile.toGeoJson(
prettyPrinted: true,
layerProperty: disableOutputLayerProperty ? nil : propertyName)
layerProperty: disableOutputLayerProperty ? nil : propertyName,
options: exportOptions)
else { throw CLIError("Failed to extract the tile data as GeoJSON") }

print(String(data: data, encoding: .utf8) ?? "", terminator: "")
Expand Down
56 changes: 40 additions & 16 deletions Sources/MVTCLI/Export.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,16 @@ extension CLI {
completion: .file(extensions: ["geojson", "json"]))
var outputFile: String

@Option(
name: [.customLong("oC", withSingleDash: true), .long],
help: "Output file compression level, between 0=none to 9=best. (default: none)")
var compressionLevel: Int?

@Option(
name: [.customLong("oSm", withSingleDash: true), .long],
help: "Simplify output features using meters.")
var simplifyMeters: Int?

@Flag(
name: .shortAndLong,
help: "Overwrite existing files.")
Expand All @@ -31,9 +41,9 @@ extension CLI {
var propertyName: String = VectorTile.defaultLayerPropertyName

@Flag(
name: [.customLong("Do", withSingleDash: true), .long],
name: [.customLong("Do", withSingleDash: true), .long],
help: "Don't add the layer name (option 'property-name') as a Feature property in the output GeoJSONs.")
var disableOutputLayerProperty: Bool = false
var disableOutputLayerProperty = false

@Flag(
name: .shortAndLong,
Expand All @@ -57,19 +67,6 @@ extension CLI {
let layerAllowlist = layer.nonempty
let outputUrl = URL(fileURLWithPath: outputFile)

if options.verbose {
print("Dumping tile '\(url.lastPathComponent)' [\(x),\(y)]@\(z) to '\(outputUrl.lastPathComponent)'")
print("Property name: \(propertyName)")

if disableOutputLayerProperty {
print(" - disable output layer property")
}

if let layerAllowlist {
print("Layers: '\(layerAllowlist.joined(separator: ","))'")
}
}

if (try? outputUrl.checkResourceIsReachable()) ?? false {
if forceOverwrite {
if options.verbose {
Expand All @@ -90,9 +87,36 @@ extension CLI {
logger: options.verbose ? CLI.logger : nil)
else { throw CLIError("Failed to parse the resource at '\(path)'") }

var exportOptions = VectorTile.ExportOptions()
if let simplifyMeters, simplifyMeters > 0 {
exportOptions.simplifyFeatures = .meters(Double(simplifyMeters))
}
if let compressionLevel, compressionLevel > 0 {
exportOptions.compression = .level(max(0, min(9, compressionLevel)))
}

if options.verbose {
print("Dumping tile '\(url.lastPathComponent)' [\(x),\(y)]@\(z) to '\(outputUrl.lastPathComponent)'")

print("Layer property name: \(propertyName)")
if disableOutputLayerProperty {
print(" - disable output layer property")
}

if let layerAllowlist {
print("Layers: '\(layerAllowlist.joined(separator: ","))'")
}

print("Output options:")
print(" - Pretty print: \(prettyPrint)")
print(" - Compression: \(exportOptions.compression)")
print(" - Simplification: \(exportOptions.simplifyFeatures)")
}

guard let data = tile.toGeoJson(
prettyPrinted: prettyPrint,
layerProperty: disableOutputLayerProperty ? nil : propertyName)
layerProperty: disableOutputLayerProperty ? nil : propertyName,
options: exportOptions)
else { throw CLIError("Failed to extract the tile data as GeoJSON") }

try data.write(to: outputUrl, options: .atomic)
Expand Down
83 changes: 75 additions & 8 deletions Sources/MVTCLI/Import.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,31 @@ extension CLI {
completion: .file(extensions: ["pbf", "mvt"]))
var outputFile: String

@Option(
name: [.customLong("oC", withSingleDash: true), .long],
help: "Output file compression level, between 0=none to 9=best.")
var compressionLevel = 9

@Option(
name: [.customLong("oBe", withSingleDash: true), .long],
help: "Output buffer extents for tiles of size \(VectorTile.ExportOptions.extent). (default: 512)")
var bufferExtents: Int?

@Option(
name: [.customLong("oBp", withSingleDash: true), .long],
help: "Output buffer pixels for tiles of size \(VectorTile.ExportOptions.tileSize). Overrides 'buffer-extents'.")
var bufferPixels: Int?

@Option(
name: [.customLong("oSe", withSingleDash: true), .long],
help: "Simplify output features using tile extents. (default: no simplification)")
var simplifyExtents: Int?

@Option(
name: [.customLong("oSm", withSingleDash: true), .long],
help: "Simplify output features using meters. Overrides 'simplify-extents'.")
var simplifyMeters: Int?

@Flag(
name: .shortAndLong,
help: "Overwrite an existing 'output' file.")
Expand Down Expand Up @@ -44,7 +69,7 @@ extension CLI {
@Flag(
name: [.customLong("Di", withSingleDash: true), .long],
help: "Don't parse the layer name (option 'property-name') from Feature properties in the input GeoJSONs, just use 'layer-name' or a default. Might speed up GeoJSON parsing considerably.")
var disableInputLayerProperty: Bool = false
var disableInputLayerProperty = false

@OptionGroup
var xyzOptions: XYZOptions
Expand Down Expand Up @@ -101,16 +126,22 @@ extension CLI {
guard var tile else { throw CLIError("Failed to create a tile [\(x),\(y)]@\(z)") }

if options.verbose {
print("Import into \(tile.origin) tile '\(outputUrl.lastPathComponent)' [\(x),\(y)]@\(z)")
print("Property name: \(propertyName)")
print("Import into \(tile.origin == .none ? "new" : tile.origin.rawValue) tile '\(outputUrl.lastPathComponent)' [\(x),\(y)]@\(z)")

print("Layer property name: \(propertyName)")
if disableInputLayerProperty {
print(" - disable input layer property")
}

if let layerName {
print("Fallback layer name: \(layerName)")
}

if !disableInputLayerProperty,
let layerAllowlist
{
print("Layers: '\(layerAllowlist.joined(separator: ","))'")
}
}

for path in other {
Expand All @@ -132,7 +163,7 @@ extension CLI {
throw CLIError("Failed to parse the GeoJSON at '\(path)'")
}

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

if !disableInputLayerProperty,
let layerAllowlist
Expand All @@ -149,12 +180,48 @@ extension CLI {
layerProperty: disableInputLayerProperty ? nil : propertyName)
}

// Export

let bufferSize: VectorTile.ExportOptions.BufferSizeOptions = if let bufferPixels, bufferPixels > 0 {
.pixel(bufferPixels)
}
else if let bufferExtents, bufferExtents > 0 {
.extent(bufferExtents)
}
else {
.extent(512)
}

var compression: VectorTile.ExportOptions.CompressionOptions = .no
if compressionLevel > 0 {
compression = .level(max(0, min(9, compressionLevel)))
}

let simplifyFeatures: VectorTile.ExportOptions.SimplifyFeaturesOptions = if let simplifyMeters, simplifyMeters > 0 {
.meters(Double(simplifyMeters))
}
else if let simplifyExtents, simplifyExtents > 0 {
.extent(simplifyExtents)
}
else {
.no
}

let exportOptions: VectorTile.ExportOptions = .init(
bufferSize: bufferSize,
compression: compression,
simplifyFeatures: simplifyFeatures)

if options.verbose {
print("Output options:")
print(" - Buffer size: \(exportOptions.bufferSize)")
print(" - Compression: \(exportOptions.compression)")
print(" - Simplification: \(exportOptions.simplifyFeatures)")
}

tile.write(
to: outputUrl,
options: .init(
bufferSize: .extent(512),
compression: .level(9),
simplifyFeatures: .no))
options: exportOptions)

if options.verbose {
print("Done.")
Expand Down
Loading

0 comments on commit f264491

Please sign in to comment.