imposm3/mapping/config.go

386 lines
9.2 KiB
Go

package mapping
import (
"errors"
"fmt"
"io/ioutil"
"github.com/omniscale/imposm3/element"
"gopkg.in/yaml.v2"
)
type Field struct {
Name string `yaml:"name"`
Key Key `yaml:"key"`
Keys []Key `yaml:"keys"`
Type string `yaml:"type"`
Args map[string]interface{} `yaml:"args"`
FromMember bool `yaml:"from_member"`
}
type Table struct {
Name string
Type TableType `yaml:"type"`
Mapping KeyValues `yaml:"mapping"`
Mappings map[string]SubMapping `yaml:"mappings"`
TypeMappings TypeMappings `yaml:"type_mappings"`
Fields []*Field `yaml:"columns"` // TODO rename Fields internaly to Columns
OldFields []*Field `yaml:"fields"`
Filters *Filters `yaml:"filters"`
RelationTypes []string `yaml:"relation_types"`
}
type GeneralizedTable struct {
Name string
SourceTableName string `yaml:"source"`
Tolerance float64 `yaml:"tolerance"`
SqlFilter string `yaml:"sql_filter"`
}
type Filters struct {
ExcludeTags *[][]string `yaml:"exclude_tags"`
}
type Tables map[string]*Table
type GeneralizedTables map[string]*GeneralizedTable
type Mapping struct {
Tables Tables `yaml:"tables"`
GeneralizedTables GeneralizedTables `yaml:"generalized_tables"`
Tags Tags `yaml:"tags"`
Areas Areas `yaml:"areas"`
// SingleIdSpace mangles the overlapping node/way/relation IDs
// to be unique (nodes positive, ways negative, relations negative -1e17)
SingleIdSpace bool `yaml:"use_single_id_space"`
}
type Areas struct {
AreaTags []Key `yaml:"area_tags"`
LinearTags []Key `yaml:"linear_tags"`
}
type Tags struct {
LoadAll bool `yaml:"load_all"`
Exclude []Key `yaml:"exclude"`
Include []Key `yaml:"include"`
}
type orderedValue struct {
value Value
order int
}
type KeyValues map[Key][]orderedValue
func (kv *KeyValues) UnmarshalYAML(unmarshal func(interface{}) error) error {
if *kv == nil {
*kv = make(map[Key][]orderedValue)
}
slice := yaml.MapSlice{}
err := unmarshal(&slice)
if err != nil {
return err
}
order := 0
for _, item := range slice {
k, ok := item.Key.(string)
if !ok {
return fmt.Errorf("mapping key '%s' not a string", k)
}
values, ok := item.Value.([]interface{})
if !ok {
return fmt.Errorf("mapping key '%s' not a string", k)
}
for _, v := range values {
if v, ok := v.(string); ok {
(*kv)[Key(k)] = append((*kv)[Key(k)], orderedValue{value: Value(v), order: order})
} else {
return fmt.Errorf("mapping value '%s' not a string", v)
}
order += 1
}
}
return nil
}
type SubMapping struct {
Mapping KeyValues
}
type TypeMappings struct {
Points KeyValues `yaml:"points"`
LineStrings KeyValues `yaml:"linestrings"`
Polygons KeyValues `yaml:"polygons"`
}
type ElementFilter func(tags element.Tags, key Key, closed bool) bool
type TagTables map[Key]map[Value][]OrderedDestTable
type DestTable struct {
Name string
SubMapping string
}
type OrderedDestTable struct {
DestTable
order int
}
type TableType string
func (tt *TableType) UnmarshalJSON(data []byte) error {
switch string(data) {
case "":
return errors.New("missing table type")
case `"point"`:
*tt = PointTable
case `"linestring"`:
*tt = LineStringTable
case `"polygon"`:
*tt = PolygonTable
case `"geometry"`:
*tt = GeometryTable
case `"relation"`:
*tt = RelationTable
case `"relation_member"`:
*tt = RelationMemberTable
default:
return errors.New("unknown type " + string(data))
}
return nil
}
const (
PolygonTable TableType = "polygon"
LineStringTable TableType = "linestring"
PointTable TableType = "point"
GeometryTable TableType = "geometry"
RelationTable TableType = "relation"
RelationMemberTable TableType = "relation_member"
)
func NewMapping(filename string) (*Mapping, error) {
f, err := ioutil.ReadFile(filename)
if err != nil {
return nil, err
}
mapping := Mapping{}
err = yaml.Unmarshal(f, &mapping)
if err != nil {
return nil, err
}
err = mapping.prepare()
if err != nil {
return nil, err
}
return &mapping, nil
}
func (m *Mapping) prepare() error {
for name, t := range m.Tables {
t.Name = name
if t.OldFields != nil {
// todo deprecate 'fields'
t.Fields = t.OldFields
}
}
for name, t := range m.GeneralizedTables {
t.Name = name
}
return nil
}
func (tt TagTables) addFromMapping(mapping KeyValues, table DestTable) {
for key, vals := range mapping {
for _, v := range vals {
vals, ok := tt[key]
tbl := OrderedDestTable{DestTable: table, order: v.order}
if ok {
vals[v.value] = append(vals[v.value], tbl)
} else {
tt[key] = make(map[Value][]OrderedDestTable)
tt[key][v.value] = append(tt[key][v.value], tbl)
}
}
}
}
func (m *Mapping) mappings(tableType TableType, mappings TagTables) {
for name, t := range m.Tables {
if t.Type != GeometryTable && t.Type != tableType {
continue
}
mappings.addFromMapping(t.Mapping, DestTable{Name: name})
for subMappingName, subMapping := range t.Mappings {
mappings.addFromMapping(subMapping.Mapping, DestTable{Name: name, SubMapping: subMappingName})
}
switch tableType {
case PointTable:
mappings.addFromMapping(t.TypeMappings.Points, DestTable{Name: name})
case LineStringTable:
mappings.addFromMapping(t.TypeMappings.LineStrings, DestTable{Name: name})
case PolygonTable:
mappings.addFromMapping(t.TypeMappings.Polygons, DestTable{Name: name})
}
}
}
func (m *Mapping) tables(tableType TableType) map[string]*TableFields {
result := make(map[string]*TableFields)
for name, t := range m.Tables {
if t.Type == tableType || t.Type == GeometryTable {
result[name] = t.TableFields()
}
}
return result
}
func (m *Mapping) extraTags(tableType TableType, tags map[Key]bool) {
for _, t := range m.Tables {
if t.Type != tableType && t.Type != GeometryTable {
continue
}
for _, field := range t.Fields {
if field.Key != "" {
tags[field.Key] = true
}
for _, k := range field.Keys {
tags[k] = true
}
}
if t.Filters != nil && t.Filters.ExcludeTags != nil {
for _, keyVal := range *t.Filters.ExcludeTags {
tags[Key(keyVal[0])] = true
}
}
if tableType == PolygonTable || tableType == RelationTable || tableType == RelationMemberTable {
if t.RelationTypes != nil {
tags["type"] = true
}
}
}
for _, k := range m.Tags.Include {
tags[k] = true
}
// always include area tag for closed-way handling
tags["area"] = true
}
type tableFilters map[string][]ElementFilter
func (m *Mapping) addTypedFilters(tableType TableType, filters tableFilters) {
var areaTags map[Key]struct{}
var linearTags map[Key]struct{}
if m.Areas.AreaTags != nil {
areaTags = make(map[Key]struct{})
for _, tag := range m.Areas.AreaTags {
areaTags[tag] = struct{}{}
}
}
if m.Areas.LinearTags != nil {
linearTags = make(map[Key]struct{})
for _, tag := range m.Areas.LinearTags {
linearTags[tag] = struct{}{}
}
}
for name, t := range m.Tables {
if t.Type != GeometryTable && t.Type != tableType {
continue
}
if t.Type == LineStringTable && areaTags != nil {
f := func(tags element.Tags, key Key, closed bool) bool {
if closed {
if tags["area"] == "yes" {
return false
}
if tags["area"] != "no" {
if _, ok := areaTags[key]; ok {
return false
}
}
}
return true
}
filters[name] = append(filters[name], f)
}
if t.Type == PolygonTable && linearTags != nil {
f := func(tags element.Tags, key Key, closed bool) bool {
if closed && tags["area"] == "no" {
return false
}
if tags["area"] != "yes" {
if _, ok := linearTags[key]; ok {
return false
}
}
return true
}
filters[name] = append(filters[name], f)
}
}
}
func (m *Mapping) addRelationFilters(tableType TableType, filters tableFilters) {
for name, t := range m.Tables {
if t.RelationTypes != nil {
f := func(tags element.Tags, key Key, closed bool) bool {
if v, ok := tags["type"]; ok {
for _, rtype := range t.RelationTypes {
if v == rtype {
return true
}
}
}
return false
}
filters[name] = append(filters[name], f)
} else {
if t.Type == PolygonTable {
// standard mulipolygon handling (boundary and land_area are for backwards compatibility)
f := func(tags element.Tags, key Key, closed bool) bool {
if v, ok := tags["type"]; ok {
if v == "multipolygon" || v == "boundary" || v == "land_area" {
return true
}
}
return false
}
filters[name] = append(filters[name], f)
}
}
}
}
func (m *Mapping) addFilters(filters tableFilters) {
for name, t := range m.Tables {
if t.Filters == nil {
continue
}
if t.Filters.ExcludeTags != nil {
for _, filterKeyVal := range *t.Filters.ExcludeTags {
f := func(tags element.Tags, key Key, closed bool) bool {
if v, ok := tags[filterKeyVal[0]]; ok {
if filterKeyVal[1] == "__any__" || v == filterKeyVal[1] {
return false
}
}
return true
}
filters[name] = append(filters[name], f)
}
}
}
}