package mapping import ( "math" "regexp" "strconv" "strings" "github.com/omniscale/imposm3/element" "github.com/omniscale/imposm3/geom" "github.com/omniscale/imposm3/logging" "github.com/omniscale/imposm3/mapping/config" "github.com/pkg/errors" ) var log = logging.NewLogger("mapping") var AvailableColumnTypes map[string]ColumnType func init() { AvailableColumnTypes = map[string]ColumnType{ "bool": {"bool", "bool", Bool, nil, nil, false}, "boolint": {"boolint", "int8", BoolInt, nil, nil, false}, "id": {"id", "int64", Id, nil, nil, false}, "string": {"string", "string", String, nil, nil, false}, "direction": {"direction", "int8", Direction, nil, nil, false}, "integer": {"integer", "int32", Integer, nil, nil, false}, "mapping_key": {"mapping_key", "string", KeyName, nil, nil, false}, "mapping_value": {"mapping_value", "string", ValueName, nil, nil, false}, "member_id": {"member_id", "int64", nil, nil, RelationMemberID, true}, "member_role": {"member_role", "string", nil, nil, RelationMemberRole, true}, "member_type": {"member_type", "int8", nil, nil, RelationMemberType, true}, "member_index": {"member_index", "int32", nil, nil, RelationMemberIndex, true}, "geometry": {"geometry", "geometry", Geometry, nil, nil, false}, "validated_geometry": {"validated_geometry", "validated_geometry", Geometry, nil, nil, false}, "hstore_tags": {"hstore_tags", "hstore_string", nil, MakeHStoreString, nil, false}, "wayzorder": {"wayzorder", "int32", nil, MakeWayZOrder, nil, false}, "pseudoarea": {"pseudoarea", "float32", nil, MakePseudoArea, nil, false}, "area": {"area", "float32", Area, nil, nil, false}, "webmerc_area": {"webmerc_area", "float32", WebmercArea, nil, nil, false}, "zorder": {"zorder", "int32", nil, MakeZOrder, nil, false}, "enumerate": {"enumerate", "int32", nil, MakeEnumerate, nil, false}, "string_suffixreplace": {"string_suffixreplace", "string", nil, MakeSuffixReplace, nil, false}, } } type MakeValue func(string, *element.OSMElem, *geom.Geometry, Match) interface{} type MakeMemberValue func(*element.Relation, *element.Member, Match) interface{} type MakeMakeValue func(string, ColumnType, config.Column) (MakeValue, error) type Key string type Value string type ColumnType struct { Name string GoType string Func MakeValue MakeFunc MakeMakeValue MemberFunc MakeMemberValue FromMember bool } func Bool(val string, elem *element.OSMElem, geom *geom.Geometry, match Match) interface{} { if val == "" || val == "0" || val == "false" || val == "no" { return false } return true } func BoolInt(val string, elem *element.OSMElem, geom *geom.Geometry, match Match) interface{} { if val == "" || val == "0" || val == "false" || val == "no" { return 0 } return 1 } func String(val string, elem *element.OSMElem, geom *geom.Geometry, match Match) interface{} { return val } func Integer(val string, elem *element.OSMElem, geom *geom.Geometry, match Match) interface{} { v, err := strconv.ParseInt(val, 10, 32) if err != nil { return nil } return v } func Id(val string, elem *element.OSMElem, geom *geom.Geometry, match Match) interface{} { return elem.Id } func KeyName(val string, elem *element.OSMElem, geom *geom.Geometry, match Match) interface{} { return match.Key } func ValueName(val string, elem *element.OSMElem, geom *geom.Geometry, match Match) interface{} { return match.Value } func RelationMemberType(rel *element.Relation, member *element.Member, match Match) interface{} { return member.Type } func RelationMemberRole(rel *element.Relation, member *element.Member, match Match) interface{} { return member.Role } func RelationMemberID(rel *element.Relation, member *element.Member, match Match) interface{} { return member.Id } func RelationMemberIndex(rel *element.Relation, member *element.Member, match Match) interface{} { for i := range rel.Members { if rel.Members[i].Id == member.Id { return i } } return -1 } func Direction(val string, elem *element.OSMElem, geom *geom.Geometry, match Match) interface{} { if val == "1" || val == "yes" || val == "true" { return 1 } else if val == "-1" { return -1 } else { return 0 } } func Geometry(val string, elem *element.OSMElem, geom *geom.Geometry, match Match) interface{} { return string(geom.Wkb) } func MakePseudoArea(columnName string, columnType ColumnType, column config.Column) (MakeValue, error) { log.Print("warn: pseudoarea type is deprecated and will be removed. See area and webmerc_area type.") return Area, nil } func Area(val string, elem *element.OSMElem, geom *geom.Geometry, match Match) interface{} { if geom.Geom == nil { return nil } area := geom.Geom.Area() if area == 0.0 { return nil } return float32(area) } func WebmercArea(val string, elem *element.OSMElem, geom *geom.Geometry, match Match) interface{} { if geom.Geom == nil { return nil } area := geom.Geom.Area() if area == 0.0 { return nil } bounds := geom.Geom.Bounds() midY := bounds.MinY + (bounds.MaxY-bounds.MinY)/2 pole := 6378137 * math.Pi // 20037508.342789244 midLat := 2*math.Atan(math.Exp((midY/pole)*math.Pi)) - math.Pi/2 area = area * math.Pow(math.Cos(midLat), 2) return float32(area) } var hstoreReplacer = strings.NewReplacer("\\", "\\\\", "\"", "\\\"") func MakeHStoreString(columnName string, columnType ColumnType, column config.Column) (MakeValue, error) { var includeAll bool var err error var include map[string]int if _, ok := column.Args["include"]; !ok { includeAll = true } else { include, err = decodeEnumArg(column, "include") if err != nil { return nil, err } } hstoreString := func(val string, elem *element.OSMElem, geom *geom.Geometry, match Match) interface{} { tags := make([]string, 0, len(elem.Tags)) for k, v := range elem.Tags { if includeAll || include[k] != 0 { tags = append(tags, `"`+hstoreReplacer.Replace(k)+`"=>"`+hstoreReplacer.Replace(v)+`"`) } } return strings.Join(tags, ", ") } return hstoreString, nil } func MakeWayZOrder(columnName string, columnType ColumnType, column config.Column) (MakeValue, error) { if _, ok := column.Args["ranks"]; !ok { return DefaultWayZOrder, nil } ranks, err := decodeEnumArg(column, "ranks") if err != nil { return nil, err } levelOffset := len(ranks) defaultRank := 0 if val, ok := column.Args["default"].(float64); ok { defaultRank = int(val) } wayZOrder := func(val string, elem *element.OSMElem, geom *geom.Geometry, match Match) interface{} { var z int layer, _ := strconv.ParseInt(elem.Tags["layer"], 10, 64) z += int(layer) * levelOffset rank, ok := ranks[match.Value] if !ok { z += defaultRank } z += rank tunnel := elem.Tags["tunnel"] if tunnel == "true" || tunnel == "yes" || tunnel == "1" { z -= levelOffset } bridge := elem.Tags["bridge"] if bridge == "true" || bridge == "yes" || bridge == "1" { z += levelOffset } return z } return wayZOrder, nil } var defaultRanks map[string]int func init() { defaultRanks = map[string]int{ "minor": 3, "road": 3, "unclassified": 3, "residential": 3, "tertiary_link": 3, "tertiary": 4, "secondary_link": 3, "secondary": 5, "primary_link": 3, "primary": 6, "trunk_link": 3, "trunk": 8, "motorway_link": 3, "motorway": 9, } } func DefaultWayZOrder(val string, elem *element.OSMElem, geom *geom.Geometry, match Match) interface{} { var z int layer, _ := strconv.ParseInt(elem.Tags["layer"], 10, 64) z += int(layer) * 10 rank := defaultRanks[match.Value] if rank == 0 { if _, ok := elem.Tags["railway"]; ok { rank = 7 } } z += rank tunnel := elem.Tags["tunnel"] if tunnel == "true" || tunnel == "yes" || tunnel == "1" { z -= 10 } bridge := elem.Tags["bridge"] if bridge == "true" || bridge == "yes" || bridge == "1" { z += 10 } return z } func MakeZOrder(columnName string, columnType ColumnType, column config.Column) (MakeValue, error) { log.Print("warn: zorder type is deprecated and will be removed. See enumerate type.") _rankList, ok := column.Args["ranks"] if !ok { return nil, errors.New("missing ranks in args for zorder") } rankList, ok := _rankList.([]interface{}) if !ok { return nil, errors.New("ranks in args for zorder not a list") } var key string _key, ok := column.Args["key"] if ok { key, ok = _key.(string) if !ok { return nil, errors.New("key in args for zorder not a string") } } ranks := make(map[string]int) for i, rank := range rankList { rankName, ok := rank.(string) if !ok { return nil, errors.New("rank in ranks not a string") } ranks[rankName] = len(rankList) - i } zOrder := func(val string, elem *element.OSMElem, geom *geom.Geometry, match Match) interface{} { if key != "" { if r, ok := ranks[elem.Tags[key]]; ok { return r } return 0 } if r, ok := ranks[match.Value]; ok { return r } return 0 } return zOrder, nil } func MakeEnumerate(columnName string, columnType ColumnType, column config.Column) (MakeValue, error) { values, err := decodeEnumArg(column, "values") if err != nil { return nil, err } enumerate := func(val string, elem *element.OSMElem, geom *geom.Geometry, match Match) interface{} { if column.Key != "" { if r, ok := values[val]; ok { return r } return 0 } if r, ok := values[match.Value]; ok { return r } return 0 } return enumerate, nil } func decodeEnumArg(column config.Column, key string) (map[string]int, error) { _valuesList, ok := column.Args[key] if !ok { return nil, errors.Errorf("missing '%v' in args for %s", key, column.Type) } valuesList, ok := _valuesList.([]interface{}) if !ok { return nil, errors.Errorf("'%v' in args for %s not a list", key, column.Type) } values := make(map[string]int) for i, value := range valuesList { valueName, ok := value.(string) if !ok { return nil, errors.Errorf("value in '%v' not a string", key) } values[valueName] = i + 1 } return values, nil } func MakeSuffixReplace(columnName string, columnType ColumnType, column config.Column) (MakeValue, error) { _changes, ok := column.Args["suffixes"] if !ok { return nil, errors.New("missing suffixes in args for string_suffixreplace") } changes, ok := _changes.(map[interface{}]interface{}) if !ok { return nil, errors.New("suffixes in args for string_suffixreplace not a dict") } strChanges := make(map[string]string, len(changes)) for k, v := range changes { _, kok := k.(string) _, vok := v.(string) if !kok || !vok { return nil, errors.New("suffixes in args for string_suffixreplace not strings") } strChanges[k.(string)] = v.(string) } var suffixes []string for k, _ := range strChanges { suffixes = append(suffixes, k) } reStr := `(` + strings.Join(suffixes, "|") + `)\b` re := regexp.MustCompile(reStr) replFunc := func(match string) string { return strChanges[match] } suffixReplace := func(val string, elem *element.OSMElem, geom *geom.Geometry, match Match) interface{} { if val != "" { return re.ReplaceAllStringFunc(val, replFunc) } return val } return suffixReplace, nil }