Closed 1ec5 closed 7 years ago
The tool now generates more-or-less an AST of the statements necessary to reproduce the style commands. By default, it outputs the AST in JSON format, but the --language
option translates the tree to Objective-C, Swift, or AppleScript.
The Swift is far from idiomatic. It doesn’t take advantage of type inference, which would simplify the code greatly:
_ = {
let compositeSource = mapView.style.source(withIdentifier: "composite")!
let placeResidentialLayer = MGLSymbolStyleLayer(identifier: "place-residential", source: compositeSource)
placeResidentialLayer.textLineHeight = MGLStyleValue<NSNumber>(rawValue: 1.2)
placeResidentialLayer.textSize = MGLStyleValue<NSNumber>(stops: [
10: MGLStyleValue<NSNumber>(rawValue: 11),
18: MGLStyleValue<NSNumber>(rawValue: 14),
])
placeResidentialLayer.maximumTextAngle = MGLStyleValue<NSNumber>(rawValue: 38)
placeResidentialLayer.symbolSpacing = MGLStyleValue<NSNumber>(rawValue: 250)
placeResidentialLayer.textFont = MGLStyleValue<NSArray>(rawValue: ["DIN Offc Pro Regular", "Arial Unicode MS Regular"])
placeResidentialLayer.textPadding = MGLStyleValue<NSNumber>(rawValue: 2)
placeResidentialLayer.textOffset = MGLStyleValue<NSValue>(rawValue: NSValue(cgVector: CGVector(dx: 0, dy: 0)))
placeResidentialLayer.textRotationAlignment = MGLStyleValue<NSValue>(rawValue: NSValue(mglTextRotationAlignment: .viewport))
placeResidentialLayer.textField = MGLStyleValue<NSString>(rawValue: "{name_en}")
placeResidentialLayer.maximumTextWidth = MGLStyleValue<NSNumber>(rawValue: 7)
placeResidentialLayer.textColor = MGLStyleValue<UIColor>(rawValue: UIColor(red: 102 / 255.0, green: 79 / 255.0, blue: 61 / 255.0, alpha: 1))
placeResidentialLayer.textHaloColor = MGLStyleValue<UIColor>(rawValue: UIColor(red: 255 / 255.0, green: 255 / 255.0, blue: 255 / 255.0, alpha: 1))
placeResidentialLayer.textHaloWidth = MGLStyleValue<NSNumber>(rawValue: 1)
placeResidentialLayer.textHaloBlur = MGLStyleValue<NSNumber>(rawValue: 0.5)
let placeNeighbourhoodLayer = mapView.style.layer(withIdentifier: "place-neighbourhood")!
mapView.style.insertLayer(placeResidentialLayer, below: placeNeighbourhoodLayer)
let mountainPeakLabelWithElevationLayer = MGLSymbolStyleLayer(identifier: "mountain-peak-label-with-elevation", source: compositeSource)
mountainPeakLabelWithElevationLayer.textLineHeight = MGLStyleValue<NSNumber>(rawValue: 1.1)
mountainPeakLabelWithElevationLayer.textSize = MGLStyleValue<NSNumber>(stops: [
10: MGLStyleValue<NSNumber>(rawValue: 11),
18: MGLStyleValue<NSNumber>(rawValue: 14),
])
mountainPeakLabelWithElevationLayer.iconImageName = MGLStyleValue<NSString>(rawValue: "{maki}-15")
mountainPeakLabelWithElevationLayer.textFont = MGLStyleValue<NSArray>(rawValue: ["DIN Offc Pro Medium", "Arial Unicode MS Regular"])
mountainPeakLabelWithElevationLayer.textOffset = MGLStyleValue<NSValue>(rawValue: NSValue(cgVector: CGVector(dx: 0, dy: 0.65)))
mountainPeakLabelWithElevationLayer.textAnchor = MGLStyleValue<NSValue>(rawValue: NSValue(mglTextAnchor: .top))
mountainPeakLabelWithElevationLayer.textField = MGLStyleValue<NSString>(rawValue: "{name_en}, {elevation_m}m")
mountainPeakLabelWithElevationLayer.textLetterSpacing = MGLStyleValue<NSNumber>(rawValue: 0.01)
mountainPeakLabelWithElevationLayer.maximumTextWidth = MGLStyleValue<NSNumber>(rawValue: 8)
mountainPeakLabelWithElevationLayer.textColor = MGLStyleValue<UIColor>(rawValue: UIColor(red: 34 / 255.0, green: 102 / 255.0, blue: 0 / 255.0, alpha: 1))
mountainPeakLabelWithElevationLayer.textHaloColor = MGLStyleValue<UIColor>(rawValue: UIColor(red: 255 / 255.0, green: 255 / 255.0, blue: 255 / 255.0, alpha: 1))
mountainPeakLabelWithElevationLayer.textHaloWidth = MGLStyleValue<NSNumber>(rawValue: 0.5)
mountainPeakLabelWithElevationLayer.textHaloBlur = MGLStyleValue<NSNumber>(rawValue: 0.5)
let poiScalerank2Layer = mapView.style.layer(withIdentifier: "poi-scalerank2")!
mapView.style.insertLayer(mountainPeakLabelWithElevationLayer, below: poiScalerank2Layer)
}()
…
_ = {
let bridgeOnewayArrowsWhiteLayer = mapView.style.layer(withIdentifier: "bridge-oneway-arrows-white") as! MGLVectorStyleLayer
bridgeOnewayArrowsWhiteLayer.predicate = NSPredicate(format: "%K = 'LineString' AND (class IN {'link', 'trunk'} AND oneway = 'true' AND structure = 'bridge' AND NOT type IN {'primary_link', 'secondary_link', 'tertiary_link'})", "$type")
let aerialwayLayer = mapView.style.layer(withIdentifier: "aerialway")!
mapView.style.removeLayer(aerialwayLayer)
}()
And for fun, AppleScript is almost there too:
set compositeSource to mapView's style's sourceWithIdentifier:"composite"
set placeResidentialLayer to (the current application's MGLSymbolStyleLayer's alloc)'s initWithIdentifier:"place-residential" source:compositeSource
tell placeResidentialLayer
set its textLineHeight to the current application's MGLStyleValue's valueWithRawValue:1.2
set its textSize to the current application's MGLStyleValue's valueWithStops:{ ¬
|10|: the current application's MGLStyleValue's valueWithRawValue:11, ¬
|18|: the current application's MGLStyleValue's valueWithRawValue:14 ¬
}
set its maximumTextAngle to the current application's MGLStyleValue's valueWithRawValue:38
set its symbolSpacing to the current application's MGLStyleValue's valueWithRawValue:250
set its textFont to the current application's MGLStyleValue's valueWithRawValue:{"DIN Offc Pro Regular", "Arial Unicode MS Regular"}
set its textPadding to the current application's MGLStyleValue's valueWithRawValue:2
set its textOffset to the current application's MGLStyleValue's valueWithRawValue:(the current application's NSValue's valueWithCGVector:{0, 0})
set its textRotationAlignment to the current application's MGLStyleValue's valueWithRawValue:(the current application's NSValue's valueWithMGLTextRotationAlignment:the current application's MGLTextRotationAlignmentViewport)
set its textField to the current application's MGLStyleValue's valueWithRawValue:"{name_en}"
set its maximumTextWidth to the current application's MGLStyleValue's valueWithRawValue:7
end tell
tell placeResidentialLayer
set its textColor to the current application's MGLStyleValue's valueWithRawValue:(the current application's UIColor's colorWithRed:102 / 255.0 green:79 / 255.0 blue:61 / 255.0 alpha:1)
set its textHaloColor to the current application's MGLStyleValue's valueWithRawValue:(the current application's UIColor's colorWithRed:255 / 255.0 green:255 / 255.0 blue:255 / 255.0 alpha:1)
set its textHaloWidth to the current application's MGLStyleValue's valueWithRawValue:1
set its textHaloBlur to the current application's MGLStyleValue's valueWithRawValue:0.5
end tell
set placeNeighbourhoodLayer to mapView's style's layerWithIdentifier:"place-neighbourhood"
tell mapView's style to insertLayer:placeResidentialLayer belowLayer:placeNeighbourhoodLayer
set mountainPeakLabelWithElevationLayer to (the current application's MGLSymbolStyleLayer's alloc)'s initWithIdentifier:"mountain-peak-label-with-elevation" source:compositeSource
tell mountainPeakLabelWithElevationLayer
set its textLineHeight to the current application's MGLStyleValue's valueWithRawValue:1.1
set its textSize to the current application's MGLStyleValue's valueWithStops:{ ¬
|10|: the current application's MGLStyleValue's valueWithRawValue:11, ¬
|18|: the current application's MGLStyleValue's valueWithRawValue:14 ¬
}
set its iconImageName to the current application's MGLStyleValue's valueWithRawValue:"{maki}-15"
set its textFont to the current application's MGLStyleValue's valueWithRawValue:{"DIN Offc Pro Medium", "Arial Unicode MS Regular"}
set its textOffset to the current application's MGLStyleValue's valueWithRawValue:(the current application's NSValue's valueWithCGVector:{0, 0.65})
set its textAnchor to the current application's MGLStyleValue's valueWithRawValue:(the current application's NSValue's valueWithMGLTextAnchor:the current application's MGLTextAnchorTop)
set its textField to the current application's MGLStyleValue's valueWithRawValue:"{name_en}, {elevation_m}m"
set its textLetterSpacing to the current application's MGLStyleValue's valueWithRawValue:0.01
set its maximumTextWidth to the current application's MGLStyleValue's valueWithRawValue:8
end tell
tell mountainPeakLabelWithElevationLayer
set its textColor to the current application's MGLStyleValue's valueWithRawValue:(the current application's UIColor's colorWithRed:34 / 255.0 green:102 / 255.0 blue:0 / 255.0 alpha:1)
set its textHaloColor to the current application's MGLStyleValue's valueWithRawValue:(the current application's UIColor's colorWithRed:255 / 255.0 green:255 / 255.0 blue:255 / 255.0 alpha:1)
set its textHaloWidth to the current application's MGLStyleValue's valueWithRawValue:0.5
set its textHaloBlur to the current application's MGLStyleValue's valueWithRawValue:0.5
end tell
set poiScalerank2Layer to mapView's style's layerWithIdentifier:"poi-scalerank2"
tell mapView's style to insertLayer:mountainPeakLabelWithElevationLayer belowLayer:poiScalerank2Layer
…
set bridgeOnewayArrowsWhiteLayer to mapView's style's layerWithIdentifier:"bridge-oneway-arrows-white"
set bridgeOnewayArrowsWhiteLayer's iconOpacity to the current application's MGLStyleValue's valueWithRawValue:0.5
set bridgeOnewayArrowsWhiteLayer to mapView's style's layerWithIdentifier:"bridge-oneway-arrows-white"
set bridgeOnewayArrowsWhiteLayer's predicate to the current application's NSPredicate's predicateWithFormat_("%K = 'LineString' AND (class IN {'link', 'trunk'} AND oneway = 'true' AND structure = 'bridge' AND NOT type IN {'primary_link', 'secondary_link', 'tertiary_link'})", "$type")
set aerialwayLayer to mapView's style's layerWithIdentifier:"aerialway"
tell mapView's style to removeLayer:aerialwayLayer
What are some use cases you imagine this tool addressing?
Tighter integration between Mapbox Studio and the iOS and macOS SDKs in the typical runtime styling development workflow.
Near as I can tell, the CI errors are caused by this repository’s eslint rules differing from those of GL JS, specifically the omission of no-var
and es6
. How do I enable those flags for just this file?
@1ec5 It's easiest for me if you stick with this repository's eslint rules for the time being.
Closing. We intend to merge this repository into the mapbox/mapbox-gl-js repository imminently, and it doesn’t make sense to hold up that merge for this proof of concept.
My next step is to issue a similar PR against gl-native for this tool. It makes more sense for this tool to live alongside the formal APIs that its generated output makes use of; by contrast, its only relation to GL JS is that it’s written in the same language. Keeping the tool in the gl-native repository will enable it to draw directly from its existing runtime styling codegen files, allowing us to keep this tool in sync with the iOS and macOS SDKs with minimal overhead.
We’ll need to find a way for a JavaScript-based tool like Studio to make use of the tool. If it would be problematic for Studio to depend on gl-native, then we can put the tool in a separate repository that depends on gl-native either formally or informally.
This is a proof of concept of a tool for transforming a style diff into Objective-C code compatible with the runtime styling API in the Mapbox iOS SDK or Mapbox macOS SDK. The code compiles but isn’t particularly idiomatic or efficient. Here’s an example:
In order to support the properties renamed as part of mapbox/mapbox-gl-native#6098, I’ve integrated the overridden properties into the JSON specification as an
sdk-name
member on each property. Extending this tool to support Swift would require for transforming the data into an intermediate, object-oriented representation before doing any string munging.Depends on #641.
/cc @tmcw @scothis