Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#26: Output export options #29

Merged
merged 5 commits into from
Aug 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading