mirror of
https://github.com/omniscale/magnacarto.git
synced 2025-02-23 07:54:10 +01:00
201 lines
4.2 KiB
Go
201 lines
4.2 KiB
Go
// Package builder implements converter from CartoCSS to Mapnik/MapServer styles.
|
|
package builder
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"path/filepath"
|
|
|
|
"github.com/omniscale/magnacarto/color"
|
|
"github.com/omniscale/magnacarto/config"
|
|
"github.com/omniscale/magnacarto/mml"
|
|
"github.com/omniscale/magnacarto/mss"
|
|
)
|
|
|
|
// Builder builds map styles from MML and MSS files.
|
|
type Builder struct {
|
|
dstMap Map
|
|
mss []string
|
|
mml string
|
|
locator config.Locator
|
|
dumpRules io.Writer
|
|
includeInactive bool
|
|
}
|
|
|
|
// New returns a Builder
|
|
func New(mw Map) *Builder {
|
|
return &Builder{dstMap: mw, includeInactive: true}
|
|
}
|
|
|
|
// AddMSS adds another mss file to this builder.
|
|
func (b *Builder) AddMSS(mss string) {
|
|
b.mss = append(b.mss, mss)
|
|
}
|
|
|
|
// SetMML sets/overwirtes the mml file of this builder.
|
|
func (b *Builder) SetMML(mml string) {
|
|
b.mml = mml
|
|
}
|
|
|
|
// SetDumpRulesDest enables internal debuging output.
|
|
func (b *Builder) SetDumpRulesDest(w io.Writer) {
|
|
b.dumpRules = w
|
|
}
|
|
|
|
// SetIncludeInactive set whether status=off layers should be included in output.
|
|
func (b *Builder) SetIncludeInactive(includeInactive bool) {
|
|
b.includeInactive = includeInactive
|
|
}
|
|
|
|
// Build parses MML, MSS files, builds all rules and adds them to the Map.
|
|
func (b *Builder) Build() error {
|
|
layerIDs := []string{}
|
|
layers := []mml.Layer{}
|
|
|
|
var mmlObj *mml.MML
|
|
if b.mml != "" {
|
|
r, err := os.Open(b.mml)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer r.Close()
|
|
mmlObj, err = mml.Parse(r)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if len(b.mss) == 0 {
|
|
for _, s := range mmlObj.Stylesheets {
|
|
b.mss = append(b.mss, filepath.Join(filepath.Dir(b.mml), s))
|
|
}
|
|
}
|
|
|
|
for _, l := range mmlObj.Layers {
|
|
layers = append(layers, l)
|
|
layerIDs = append(layerIDs, l.ID)
|
|
}
|
|
}
|
|
|
|
carto := mss.New()
|
|
|
|
for _, mss := range b.mss {
|
|
err := carto.ParseFile(mss)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
if err := carto.Evaluate(); err != nil {
|
|
return err
|
|
}
|
|
|
|
if m, ok := b.dstMap.(MapZoomScaleSetter); ok {
|
|
if mmlObj != nil && mmlObj.Map.ZoomScales != nil {
|
|
m.SetZoomScales(mmlObj.Map.ZoomScales)
|
|
}
|
|
}
|
|
|
|
if b.mml == "" {
|
|
layerIDs = carto.MSS().Layers()
|
|
for _, layerID := range layerIDs {
|
|
layers = append(layers,
|
|
// XXX assume we only have LineStrings for -mss only export
|
|
mml.Layer{ID: layerID, Type: mml.LineString},
|
|
)
|
|
}
|
|
}
|
|
|
|
for _, l := range layers {
|
|
zoom := layerZoomRange(l)
|
|
rules := carto.MSS().LayerZoomRules(l.ID, zoom, l.Classes...)
|
|
|
|
if b.dumpRules != nil {
|
|
for _, r := range rules {
|
|
fmt.Fprintln(b.dumpRules, r.String())
|
|
}
|
|
}
|
|
if len(rules) > 0 && (l.Active || b.includeInactive) {
|
|
b.dstMap.AddLayer(l, rules)
|
|
}
|
|
}
|
|
|
|
if m, ok := b.dstMap.(MapOptionsSetter); ok {
|
|
if bgColor, ok := carto.MSS().Map().GetColor("background-color"); ok {
|
|
m.SetBackgroundColor(bgColor)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func layerZoomRange(l mml.Layer) mss.ZoomRange {
|
|
zoom := mss.InvalidZoom
|
|
minZoom, minOk := l.Properties["minzoom"].(int)
|
|
maxZoom, maxOk := l.Properties["maxzoom"].(int)
|
|
if minOk {
|
|
zoom = mss.NewZoomRange(mss.GTE, int64(minZoom))
|
|
if maxOk {
|
|
zoom = zoom & mss.NewZoomRange(mss.LTE, int64(maxZoom))
|
|
}
|
|
} else if maxOk {
|
|
zoom = mss.NewZoomRange(mss.LTE, int64(maxZoom))
|
|
}
|
|
return zoom
|
|
}
|
|
|
|
type MapOptionsSetter interface {
|
|
SetBackgroundColor(color.Color)
|
|
}
|
|
|
|
type MapZoomScaleSetter interface {
|
|
SetZoomScales([]int)
|
|
}
|
|
|
|
type Writer interface {
|
|
Write(io.Writer) error
|
|
WriteFiles(basename string) error
|
|
}
|
|
|
|
type Map interface {
|
|
AddLayer(mml.Layer, []mss.Rule)
|
|
}
|
|
|
|
type MapWriter interface {
|
|
Writer
|
|
Map
|
|
}
|
|
|
|
// BuildMapFromString parses the style from a string and adds all
|
|
// mml.Layers to the map.
|
|
func BuildMapFromString(m Map, mml *mml.MML, style string) error {
|
|
carto := mss.New()
|
|
|
|
err := carto.ParseString(style)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if err := carto.Evaluate(); err != nil {
|
|
return err
|
|
}
|
|
|
|
if m, ok := m.(MapZoomScaleSetter); ok {
|
|
if mml.Map.ZoomScales != nil {
|
|
m.SetZoomScales(mml.Map.ZoomScales)
|
|
}
|
|
}
|
|
|
|
for _, l := range mml.Layers {
|
|
zoom := layerZoomRange(l)
|
|
rules := carto.MSS().LayerZoomRules(l.ID, zoom, l.Classes...)
|
|
|
|
if len(rules) > 0 {
|
|
m.AddLayer(l, rules)
|
|
}
|
|
}
|
|
|
|
if m, ok := m.(MapOptionsSetter); ok {
|
|
if bgColor, ok := carto.MSS().Map().GetColor("background-color"); ok {
|
|
m.SetBackgroundColor(bgColor)
|
|
}
|
|
}
|
|
return nil
|
|
}
|