Add new relation_members table type
parent
1aff01cc9e
commit
423390ea71
|
@ -36,6 +36,7 @@ type Inserter interface {
|
|||
InsertPoint(element.OSMElem, geom.Geometry, []mapping.Match) error
|
||||
InsertLineString(element.OSMElem, geom.Geometry, []mapping.Match) error
|
||||
InsertPolygon(element.OSMElem, geom.Geometry, []mapping.Match) error
|
||||
InsertRelationMember(element.Relation, element.Member, geom.Geometry, []mapping.Match) error
|
||||
}
|
||||
|
||||
type Deployer interface {
|
||||
|
@ -103,6 +104,9 @@ func (n *nullDb) Abort() error
|
|||
func (n *nullDb) InsertPoint(element.OSMElem, geom.Geometry, []mapping.Match) error { return nil }
|
||||
func (n *nullDb) InsertLineString(element.OSMElem, geom.Geometry, []mapping.Match) error { return nil }
|
||||
func (n *nullDb) InsertPolygon(element.OSMElem, geom.Geometry, []mapping.Match) error { return nil }
|
||||
func (n *nullDb) InsertRelationMember(element.Relation, element.Member, geom.Geometry, []mapping.Match) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func newNullDb(conf Config, m *mapping.Mapping) (DB, error) {
|
||||
return &nullDb{}, nil
|
||||
|
|
|
@ -58,7 +58,7 @@ func createTable(tx *sql.Tx, spec TableSpec) error {
|
|||
}
|
||||
|
||||
func addGeometryColumn(tx *sql.Tx, tableName string, spec TableSpec) error {
|
||||
colName := "geometry"
|
||||
colName := ""
|
||||
for _, col := range spec.Columns {
|
||||
if col.Type.Name() == "GEOMETRY" {
|
||||
colName = col.Name
|
||||
|
@ -66,6 +66,10 @@ func addGeometryColumn(tx *sql.Tx, tableName string, spec TableSpec) error {
|
|||
}
|
||||
}
|
||||
|
||||
if colName == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
geomType := strings.ToUpper(spec.GeometryType)
|
||||
if geomType == "POLYGON" {
|
||||
geomType = "GEOMETRY" // for multipolygon support
|
||||
|
@ -480,6 +484,16 @@ func (pg *PostGIS) InsertPolygon(elem element.OSMElem, geom geom.Geometry, match
|
|||
return nil
|
||||
}
|
||||
|
||||
func (pg *PostGIS) InsertRelationMember(rel element.Relation, m element.Member, geom geom.Geometry, matches []mapping.Match) error {
|
||||
for _, match := range matches {
|
||||
row := match.MemberRow(&rel, &m, &geom)
|
||||
if err := pg.txRouter.Insert(match.Table.Name, row); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (pg *PostGIS) Delete(id int64, matches interface{}) error {
|
||||
if matches, ok := matches.([]mapping.Match); ok {
|
||||
for _, match := range matches {
|
||||
|
|
|
@ -125,11 +125,19 @@ func (spec *TableSpec) DeleteSQL() string {
|
|||
}
|
||||
|
||||
func NewTableSpec(pg *PostGIS, t *mapping.Table) *TableSpec {
|
||||
var geomType string
|
||||
switch t.Type {
|
||||
case mapping.RelationMemberTable:
|
||||
geomType = "geometry"
|
||||
default:
|
||||
geomType = string(t.Type)
|
||||
}
|
||||
|
||||
spec := TableSpec{
|
||||
Name: t.Name,
|
||||
FullName: pg.Prefix + t.Name,
|
||||
Schema: pg.Config.ImportSchema,
|
||||
GeometryType: string(t.Type),
|
||||
GeometryType: geomType,
|
||||
Srid: pg.Config.Srid,
|
||||
}
|
||||
for _, field := range t.Fields {
|
||||
|
|
|
@ -149,6 +149,8 @@ func Update(oscFile string, geometryLimiter *limit.Limiter, expireor expire.Expi
|
|||
relations,
|
||||
db, progress,
|
||||
tagmapping.PolygonMatcher(),
|
||||
tagmapping.RelationMatcher(),
|
||||
tagmapping.RelationMemberMatcher(),
|
||||
config.BaseOptions.Srid)
|
||||
relWriter.SetLimiter(geometryLimiter)
|
||||
relWriter.SetExpireor(expireor)
|
||||
|
|
|
@ -72,6 +72,8 @@ type Member struct {
|
|||
Type MemberType `json:"type"`
|
||||
Role string `json:"role"`
|
||||
Way *Way `json:"-"`
|
||||
Node *Node `json:"-"`
|
||||
Elem *OSMElem `json:"-"`
|
||||
}
|
||||
|
||||
type Relation struct {
|
||||
|
|
|
@ -182,6 +182,8 @@ func Import() {
|
|||
relations,
|
||||
db, progress,
|
||||
tagmapping.PolygonMatcher(),
|
||||
tagmapping.RelationMatcher(),
|
||||
tagmapping.RelationMemberMatcher(),
|
||||
config.BaseOptions.Srid)
|
||||
relWriter.SetLimiter(geometryLimiter)
|
||||
relWriter.EnableConcurrent()
|
||||
|
|
|
@ -11,11 +11,12 @@ import (
|
|||
)
|
||||
|
||||
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"`
|
||||
Name string `yaml:"name"`
|
||||
Key Key `yaml:"key"`
|
||||
Keys []Key `yaml:"keys"`
|
||||
Type string `yaml:"type"`
|
||||
Args map[string]interface{} `yaml:"args"`
|
||||
FromMembers bool `yaml:"from_members"`
|
||||
}
|
||||
|
||||
type Table struct {
|
||||
|
@ -133,6 +134,10 @@ func (tt *TableType) UnmarshalJSON(data []byte) error {
|
|||
*tt = PolygonTable
|
||||
case `"geometry"`:
|
||||
*tt = GeometryTable
|
||||
case `"relation"`:
|
||||
*tt = RelationTable
|
||||
case `"relation_member"`:
|
||||
*tt = RelationMemberTable
|
||||
default:
|
||||
return errors.New("unknown type " + string(data))
|
||||
}
|
||||
|
@ -140,10 +145,12 @@ func (tt *TableType) UnmarshalJSON(data []byte) error {
|
|||
}
|
||||
|
||||
const (
|
||||
PolygonTable TableType = "polygon"
|
||||
LineStringTable TableType = "linestring"
|
||||
PointTable TableType = "point"
|
||||
GeometryTable TableType = "geometry"
|
||||
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) {
|
||||
|
|
|
@ -17,26 +17,31 @@ var AvailableFieldTypes map[string]FieldType
|
|||
|
||||
func init() {
|
||||
AvailableFieldTypes = map[string]FieldType{
|
||||
"bool": {"bool", "bool", Bool, nil},
|
||||
"boolint": {"boolint", "int8", BoolInt, nil},
|
||||
"id": {"id", "int64", Id, nil},
|
||||
"string": {"string", "string", String, nil},
|
||||
"direction": {"direction", "int8", Direction, nil},
|
||||
"integer": {"integer", "int32", Integer, nil},
|
||||
"mapping_key": {"mapping_key", "string", KeyName, nil},
|
||||
"mapping_value": {"mapping_value", "string", ValueName, nil},
|
||||
"geometry": {"geometry", "geometry", Geometry, nil},
|
||||
"validated_geometry": {"validated_geometry", "validated_geometry", Geometry, nil},
|
||||
"hstore_tags": {"hstore_tags", "hstore_string", HstoreString, nil},
|
||||
"wayzorder": {"wayzorder", "int32", WayZOrder, nil},
|
||||
"pseudoarea": {"pseudoarea", "float32", PseudoArea, nil},
|
||||
"zorder": {"zorder", "int32", nil, MakeZOrder},
|
||||
"enumerate": {"enumerate", "int32", nil, MakeEnumerate},
|
||||
"string_suffixreplace": {"string_suffixreplace", "string", nil, MakeSuffixReplace},
|
||||
"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},
|
||||
"relation_member_id": {"relation_member_id", "int64", nil, nil, RelationMemberID, true},
|
||||
"relation_member_role": {"relation_member_role", "string", nil, nil, RelationMemberRole, true},
|
||||
"relation_member_type": {"relation_member_type", "int8", nil, nil, RelationMemberType, true},
|
||||
"relation_member_index": {"relation_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", HstoreString, nil, nil, false},
|
||||
"wayzorder": {"wayzorder", "int32", WayZOrder, nil, nil, false},
|
||||
"pseudoarea": {"pseudoarea", "float32", PseudoArea, 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, FieldType, Field) (MakeValue, error)
|
||||
|
||||
|
@ -55,6 +60,22 @@ func (f *FieldSpec) Value(elem *element.OSMElem, geom *geom.Geometry, match Matc
|
|||
return nil
|
||||
}
|
||||
|
||||
func (f *FieldSpec) MemberValue(rel *element.Relation, member *element.Member, geom *geom.Geometry, match Match) interface{} {
|
||||
if f.Type.Func != nil {
|
||||
if f.Type.FromMembers {
|
||||
if member.Elem == nil {
|
||||
return nil
|
||||
}
|
||||
return f.Type.Func(member.Elem.Tags[string(f.Key)], member.Elem, geom, match)
|
||||
}
|
||||
return f.Type.Func(rel.Tags[string(f.Key)], &rel.OSMElem, geom, match)
|
||||
}
|
||||
if f.Type.MemberFunc != nil {
|
||||
return f.Type.MemberFunc(rel, member, match)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type TableFields struct {
|
||||
fields []FieldSpec
|
||||
}
|
||||
|
@ -67,6 +88,14 @@ func (t *TableFields) MakeRow(elem *element.OSMElem, geom *geom.Geometry, match
|
|||
return row
|
||||
}
|
||||
|
||||
func (t *TableFields) MakeMemberRow(rel *element.Relation, member *element.Member, geom *geom.Geometry, match Match) []interface{} {
|
||||
var row []interface{}
|
||||
for _, field := range t.fields {
|
||||
row = append(row, field.MemberValue(rel, member, geom, match))
|
||||
}
|
||||
return row
|
||||
}
|
||||
|
||||
func (field *Field) FieldType() *FieldType {
|
||||
if fieldType, ok := AvailableFieldTypes[field.Type]; ok {
|
||||
if fieldType.MakeFunc != nil {
|
||||
|
@ -75,8 +104,9 @@ func (field *Field) FieldType() *FieldType {
|
|||
log.Print(err)
|
||||
return nil
|
||||
}
|
||||
fieldType = FieldType{fieldType.Name, fieldType.GoType, makeValue, nil}
|
||||
fieldType = FieldType{fieldType.Name, fieldType.GoType, makeValue, nil, nil, fieldType.FromMembers}
|
||||
}
|
||||
fieldType.FromMembers = field.FromMembers
|
||||
return &fieldType
|
||||
}
|
||||
return nil
|
||||
|
@ -101,10 +131,12 @@ func (t *Table) TableFields() *TableFields {
|
|||
}
|
||||
|
||||
type FieldType struct {
|
||||
Name string
|
||||
GoType string
|
||||
Func MakeValue
|
||||
MakeFunc MakeMakeValue
|
||||
Name string
|
||||
GoType string
|
||||
Func MakeValue
|
||||
MakeFunc MakeMakeValue
|
||||
MemberFunc MakeMemberValue
|
||||
FromMembers bool
|
||||
}
|
||||
|
||||
func Bool(val string, elem *element.OSMElem, geom *geom.Geometry, match Match) interface{} {
|
||||
|
@ -145,6 +177,27 @@ func ValueName(val string, elem *element.OSMElem, geom *geom.Geometry, match Mat
|
|||
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
|
||||
|
|
|
@ -1,29 +1,47 @@
|
|||
package mapping
|
||||
|
||||
import (
|
||||
_ "log"
|
||||
|
||||
"github.com/omniscale/imposm3/element"
|
||||
"github.com/omniscale/imposm3/geom"
|
||||
)
|
||||
|
||||
func (m *Mapping) PointMatcher() NodeMatcher {
|
||||
mappings := make(TagTables)
|
||||
m.mappings("point", mappings)
|
||||
m.mappings(PointTable, mappings)
|
||||
filters := m.ElementFilters()
|
||||
return &tagMatcher{mappings, m.tables("point"), filters, false}
|
||||
return &tagMatcher{mappings, m.tables(PointTable), filters, false}
|
||||
}
|
||||
|
||||
func (m *Mapping) LineStringMatcher() WayMatcher {
|
||||
mappings := make(TagTables)
|
||||
m.mappings("linestring", mappings)
|
||||
m.mappings(LineStringTable, mappings)
|
||||
filters := m.ElementFilters()
|
||||
return &tagMatcher{mappings, m.tables("linestring"), filters, false}
|
||||
return &tagMatcher{mappings, m.tables(LineStringTable), filters, false}
|
||||
}
|
||||
|
||||
func (m *Mapping) PolygonMatcher() RelWayMatcher {
|
||||
mappings := make(TagTables)
|
||||
m.mappings("polygon", mappings)
|
||||
m.mappings(PolygonTable, mappings)
|
||||
filters := m.ElementFilters()
|
||||
return &tagMatcher{mappings, m.tables("polygon"), filters, true}
|
||||
return &tagMatcher{mappings, m.tables(PolygonTable), filters, true}
|
||||
}
|
||||
|
||||
func (m *Mapping) RelationMatcher() RelationMatcher {
|
||||
mappings := make(TagTables)
|
||||
m.mappings(RelationTable, mappings)
|
||||
filters := m.ElementFilters()
|
||||
log.Print(mappings)
|
||||
return &tagMatcher{mappings, m.tables(RelationTable), filters, true}
|
||||
}
|
||||
|
||||
func (m *Mapping) RelationMemberMatcher() RelationMatcher {
|
||||
mappings := make(TagTables)
|
||||
m.mappings(RelationMemberTable, mappings)
|
||||
filters := m.ElementFilters()
|
||||
log.Print(mappings)
|
||||
return &tagMatcher{mappings, m.tables(RelationMemberTable), filters, true}
|
||||
}
|
||||
|
||||
type Match struct {
|
||||
|
@ -61,6 +79,10 @@ func (m *Match) Row(elem *element.OSMElem, geom *geom.Geometry) []interface{} {
|
|||
return m.tableFields.MakeRow(elem, geom, *m)
|
||||
}
|
||||
|
||||
func (m *Match) MemberRow(rel *element.Relation, member *element.Member, geom *geom.Geometry) []interface{} {
|
||||
return m.tableFields.MakeMemberRow(rel, member, geom, *m)
|
||||
}
|
||||
|
||||
func (tm *tagMatcher) MatchNode(node *element.Node) []Match {
|
||||
return tm.match(&node.Tags)
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
.PHONY: all test clean
|
||||
.PHONY: all test clean files
|
||||
|
||||
ifdef VERBOSE
|
||||
TESTOPTS = -v
|
||||
|
@ -16,12 +16,16 @@ OSCGZ_FILES=$(addprefix build/,$(patsubst %.osc,%.osc.gz,$(wildcard *.osc)))
|
|||
|
||||
build/%.pbf: %.osm
|
||||
@mkdir -p build
|
||||
osmosis --read-xml $< --sort TypeThenId --write-pbf $@ omitmetadata=true
|
||||
osmosis --read-xml $< --sort type="TypeThenId" --write-pbf $@ omitmetadata=true
|
||||
|
||||
build/%.osc.gz: %.osc
|
||||
@mkdir -p build
|
||||
gzip --stdout $< > $@
|
||||
|
||||
test: $(PBF_FILES) $(OSCGZ_FILES)
|
||||
files: $(PBF_FILES) $(OSCGZ_FILES)
|
||||
|
||||
test: files
|
||||
(cd .. && godep go test ./test $(TESTOPTS))
|
||||
|
||||
route_relation: files
|
||||
(cd .. && godep go test -test.run TestRouteRelation_ ./test $(TESTOPTS))
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<osmChange version="0.6" generator="Osmosis 0.41">
|
||||
</osmChange>
|
|
@ -0,0 +1,113 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<osm version="0.6" generator="CGImap 0.4.0 (2097 thorn-01.openstreetmap.org)" copyright="OpenStreetMap and contributors" attribution="http://www.openstreetmap.org/copyright" license="http://opendatacommons.org/licenses/odbl/1-0/">
|
||||
<node id="100101" version="1" timestamp="2015-12-31T23:59:99Z" lat="53.0" lon="8.200"/>
|
||||
<node id="100102" version="1" timestamp="2015-12-31T23:59:99Z" lat="53.0" lon="8.201"/>
|
||||
<node id="100103" version="1" timestamp="2015-12-31T23:59:99Z" lat="53.0" lon="8.202"/>
|
||||
<node id="100104" version="1" timestamp="2015-12-31T23:59:99Z" lat="53.0" lon="8.203"/>
|
||||
|
||||
<node id="100201" version="1" timestamp="2015-12-31T23:59:99Z" lat="53.0001" lon="8.2000"/>
|
||||
<node id="100211" version="1" timestamp="2015-12-31T23:59:99Z" lat="53.0001" lon="8.2001"/>
|
||||
<node id="100202" version="1" timestamp="2015-12-31T23:59:99Z" lat="53.0001" lon="8.2010"/>
|
||||
<node id="100212" version="1" timestamp="2015-12-31T23:59:99Z" lat="53.0001" lon="8.2011"/>
|
||||
<node id="100203" version="1" timestamp="2015-12-31T23:59:99Z" lat="53.0001" lon="8.2020"/>
|
||||
<node id="100213" version="1" timestamp="2015-12-31T23:59:99Z" lat="53.0001" lon="8.2021"/>
|
||||
<node id="100204" version="1" timestamp="2015-12-31T23:59:99Z" lat="53.0001" lon="8.2030"/>
|
||||
<node id="100214" version="1" timestamp="2015-12-31T23:59:99Z" lat="53.0001" lon="8.2031"/>
|
||||
|
||||
|
||||
<way id="100501" version="1" timestamp="2015-12-31T23:59:99Z">
|
||||
<nd ref="100101"/>
|
||||
<nd ref="100102"/>
|
||||
<tag k="highway" v="residential"/>
|
||||
<tag k="name" v="Residential Street"/>
|
||||
</way>
|
||||
|
||||
<way id="100502" version="1" timestamp="2015-12-31T23:59:99Z">
|
||||
<nd ref="100102"/>
|
||||
<nd ref="100103"/>
|
||||
<tag k="highway" v="residential"/>
|
||||
<tag k="name" v="Residential Street"/>
|
||||
</way>
|
||||
|
||||
<way id="100503" version="1" timestamp="2015-12-31T23:59:99Z">
|
||||
<nd ref="100103"/>
|
||||
<nd ref="100104"/>
|
||||
<!-- untagged -->
|
||||
</way>
|
||||
|
||||
<way id="100511" version="1" timestamp="2015-12-31T23:59:99Z">
|
||||
<nd ref="100201"/>
|
||||
<nd ref="100211"/>
|
||||
</way>
|
||||
|
||||
<way id="100512" version="1" timestamp="2015-12-31T23:59:99Z">
|
||||
<nd ref="100202"/>
|
||||
<nd ref="100212"/>
|
||||
</way>
|
||||
|
||||
<way id="100513" version="1" timestamp="2015-12-31T23:59:99Z">
|
||||
<nd ref="100203"/>
|
||||
<nd ref="100213"/>
|
||||
</way>
|
||||
|
||||
<way id="100514" version="1" timestamp="2015-12-31T23:59:99Z">
|
||||
<nd ref="100204"/>
|
||||
<nd ref="100214"/>
|
||||
</way>
|
||||
|
||||
<relation id="100901" version="23" timestamp="2015-06-02T04:13:19Z">
|
||||
<member type="node" ref="100101" role="stop_entry_only"/>
|
||||
<member type="way" ref="100511" role="platform_entry_only"/>
|
||||
<member type="node" ref="100102" role="stop"/>
|
||||
<member type="way" ref="100512" role="platform"/>
|
||||
<member type="node" ref="100103" role="stop"/>
|
||||
<member type="way" ref="100513" role="platform"/>
|
||||
<member type="node" ref="100104" role="stop_exit_only"/>
|
||||
<member type="way" ref="100514" role="platform_exit_only"/>
|
||||
<member type="way" ref="100501" role=""/>
|
||||
<member type="way" ref="100502" role=""/>
|
||||
<member type="way" ref="100503" role=""/>
|
||||
<tag k="colour" v="#F5A9E1"/>
|
||||
<tag k="from" v="A"/>
|
||||
<tag k="name" v="Bus 301: A => B"/>
|
||||
<tag k="network" v="ABC"/>
|
||||
<tag k="ref" v="301"/>
|
||||
<tag k="route" v="bus"/>
|
||||
<tag k="to" v="B"/>
|
||||
<tag k="type" v="route"/>
|
||||
</relation>
|
||||
|
||||
<relation id="100902" version="23" timestamp="2015-06-02T04:13:19Z">
|
||||
<member type="node" ref="100104" role="stop_entry_only"/>
|
||||
<member type="way" ref="100514" role="platform_entry_only"/>
|
||||
<member type="node" ref="100103" role="stop"/>
|
||||
<member type="way" ref="100513" role="platform"/>
|
||||
<member type="node" ref="100102" role="stop"/>
|
||||
<member type="way" ref="100512" role="platform"/>
|
||||
<member type="node" ref="100101" role="stop_exit_only"/>
|
||||
<member type="way" ref="100511" role="platform_exit_only"/>
|
||||
<member type="way" ref="100503" role=""/>
|
||||
<member type="way" ref="100502" role=""/>
|
||||
<member type="way" ref="100501" role=""/>
|
||||
<tag k="colour" v="#F5A9E1"/>
|
||||
<tag k="from" v="B"/>
|
||||
<tag k="name" v="Bus 301: B => A"/>
|
||||
<tag k="network" v="ABC"/>
|
||||
<tag k="ref" v="301"/>
|
||||
<tag k="route" v="bus"/>
|
||||
<tag k="to" v="A"/>
|
||||
<tag k="type" v="route"/>
|
||||
</relation>
|
||||
|
||||
<relation id="100903" version="23" timestamp="2015-06-02T04:13:19Z">
|
||||
<member type="relation" ref="100901" role=""/>
|
||||
<member type="relation" ref="100902" role=""/>
|
||||
<tag k="colour" v="#F5A9E1"/>
|
||||
<tag k="name" v="Bus 301"/>
|
||||
<tag k="network" v="ABC"/>
|
||||
<tag k="ref" v="301"/>
|
||||
<tag k="route_master" v="bus"/>
|
||||
<tag k="type" v="route_master"/>
|
||||
</relation>
|
||||
|
||||
</osm>
|
|
@ -0,0 +1,128 @@
|
|||
{
|
||||
"tags": {
|
||||
"load_all": true,
|
||||
"exclude": [
|
||||
"created_by",
|
||||
"source"
|
||||
]
|
||||
},
|
||||
"tables": {
|
||||
"route_members": {
|
||||
"fields": [
|
||||
{
|
||||
"type": "id",
|
||||
"name": "osm_id"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"name": "ref",
|
||||
"key": "ref"
|
||||
},
|
||||
{
|
||||
"type": "relation_member_id",
|
||||
"name": "member"
|
||||
},
|
||||
{
|
||||
"type": "relation_member_index",
|
||||
"name": "index"
|
||||
},
|
||||
{
|
||||
"type": "relation_member_role",
|
||||
"name": "role"
|
||||
},
|
||||
{
|
||||
"type": "relation_member_type",
|
||||
"name": "type"
|
||||
},
|
||||
{
|
||||
"type": "geometry",
|
||||
"name": "geometry"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"name": "name",
|
||||
"key": "name",
|
||||
"from_members": true
|
||||
}
|
||||
],
|
||||
"type": "relation_member",
|
||||
"mapping": {
|
||||
"route": [
|
||||
"bus",
|
||||
"tram",
|
||||
"rail"
|
||||
]
|
||||
}
|
||||
},
|
||||
"routes": {
|
||||
"fields": [
|
||||
{
|
||||
"type": "id",
|
||||
"name": "osm_id"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"name": "ref",
|
||||
"key": "ref"
|
||||
},
|
||||
{
|
||||
"type": "hstore_tags",
|
||||
"name": "tags"
|
||||
}
|
||||
],
|
||||
"type": "relation",
|
||||
"mapping": {
|
||||
"route": [
|
||||
"bus",
|
||||
"tram",
|
||||
"rail"
|
||||
]
|
||||
}
|
||||
},
|
||||
"master_routes": {
|
||||
"fields": [
|
||||
{
|
||||
"type": "id",
|
||||
"name": "osm_id"
|
||||
},
|
||||
{
|
||||
"type": "relation_member_id",
|
||||
"name": "member"
|
||||
},
|
||||
{
|
||||
"type": "relation_member_index",
|
||||
"name": "index"
|
||||
},
|
||||
{
|
||||
"type": "relation_member_role",
|
||||
"name": "role"
|
||||
},
|
||||
{
|
||||
"type": "relation_member_type",
|
||||
"name": "type"
|
||||
},
|
||||
{
|
||||
"type": "geometry",
|
||||
"name": "geometry"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"name": "subname",
|
||||
"key": "name",
|
||||
"from_members": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"name": "name",
|
||||
"key": "name"
|
||||
}
|
||||
],
|
||||
"type": "relation_member",
|
||||
"mapping": {
|
||||
"route_master": [
|
||||
"bus"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
package test
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
|
||||
"testing"
|
||||
|
||||
"github.com/omniscale/imposm3/geom/geos"
|
||||
)
|
||||
|
||||
func TestRouteRelation_Prepare(t *testing.T) {
|
||||
ts.dir = "/tmp/imposm3test"
|
||||
ts.config = importConfig{
|
||||
connection: "postgis://",
|
||||
cacheDir: ts.dir,
|
||||
osmFileName: "build/route_relation.pbf",
|
||||
mappingFileName: "route_relation_mapping.json",
|
||||
}
|
||||
ts.g = geos.NewGeos()
|
||||
|
||||
var err error
|
||||
ts.db, err = sql.Open("postgres", "sslmode=disable")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
ts.dropSchemas()
|
||||
}
|
||||
|
||||
func TestRouteRelation_Import(t *testing.T) {
|
||||
if ts.tableExists(t, dbschemaImport, "osm_routes") != false {
|
||||
t.Fatalf("table osm_routes exists in schema %s", dbschemaImport)
|
||||
}
|
||||
ts.importOsm(t)
|
||||
if ts.tableExists(t, dbschemaImport, "osm_routes") != true {
|
||||
t.Fatalf("table osm_routes does not exists in schema %s", dbschemaImport)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRouteRelation_Deploy(t *testing.T) {
|
||||
ts.deployOsm(t)
|
||||
if ts.tableExists(t, dbschemaImport, "osm_routes") != false {
|
||||
t.Fatalf("table osm_routes exists in schema %s", dbschemaImport)
|
||||
}
|
||||
if ts.tableExists(t, dbschemaProduction, "osm_routes") != true {
|
||||
t.Fatalf("table osm_routes does not exists in schema %s", dbschemaProduction)
|
||||
}
|
||||
}
|
||||
|
||||
// #######################################################################
|
||||
|
||||
func TestRouteRelation_Update(t *testing.T) {
|
||||
ts.updateOsm(t, "./build/route_relation.osc.gz")
|
||||
}
|
|
@ -0,0 +1,83 @@
|
|||
import psycopg2
|
||||
import psycopg2.extras
|
||||
|
||||
import helper as t
|
||||
|
||||
psycopg2.extras.register_hstore(psycopg2.connect(**t.db_conf), globally=True)
|
||||
|
||||
mapping_file = 'route_relation_mapping.json'
|
||||
|
||||
def setup():
|
||||
t.setup()
|
||||
|
||||
def teardown():
|
||||
t.teardown()
|
||||
|
||||
RELOFFSET = int(-1e17)
|
||||
|
||||
#######################################################################
|
||||
def test_import():
|
||||
"""Import succeeds"""
|
||||
t.drop_schemas()
|
||||
assert not t.table_exists('osm_routes', schema=t.TEST_SCHEMA_IMPORT)
|
||||
t.imposm3_import(t.db_conf, './build/route_relation.pbf', mapping_file)
|
||||
assert t.table_exists('osm_routes', schema=t.TEST_SCHEMA_IMPORT)
|
||||
|
||||
def test_deploy():
|
||||
"""Deploy succeeds"""
|
||||
assert not t.table_exists('osm_routes', schema=t.TEST_SCHEMA_PRODUCTION)
|
||||
t.imposm3_deploy(t.db_conf, mapping_file)
|
||||
assert t.table_exists('osm_routes', schema=t.TEST_SCHEMA_PRODUCTION)
|
||||
assert not t.table_exists('osm_routes', schema=t.TEST_SCHEMA_IMPORT)
|
||||
|
||||
#######################################################################
|
||||
|
||||
|
||||
#######################################################################
|
||||
|
||||
def test_update():
|
||||
"""Diff import applies"""
|
||||
t.imposm3_update(t.db_conf, './build/route_relation.osc.gz', mapping_file)
|
||||
|
||||
#######################################################################
|
||||
|
||||
|
||||
#######################################################################
|
||||
def test_deploy_and_revert_deploy():
|
||||
"""Revert deploy succeeds"""
|
||||
assert not t.table_exists('osm_routes', schema=t.TEST_SCHEMA_IMPORT)
|
||||
assert t.table_exists('osm_routes', schema=t.TEST_SCHEMA_PRODUCTION)
|
||||
assert not t.table_exists('osm_routes', schema=t.TEST_SCHEMA_BACKUP)
|
||||
|
||||
# import again to have a new import schema
|
||||
t.imposm3_import(t.db_conf, './build/route_relation.pbf', mapping_file)
|
||||
assert t.table_exists('osm_routes', schema=t.TEST_SCHEMA_IMPORT)
|
||||
|
||||
t.imposm3_deploy(t.db_conf, mapping_file)
|
||||
assert not t.table_exists('osm_routes', schema=t.TEST_SCHEMA_IMPORT)
|
||||
assert t.table_exists('osm_routes', schema=t.TEST_SCHEMA_PRODUCTION)
|
||||
assert t.table_exists('osm_routes', schema=t.TEST_SCHEMA_BACKUP)
|
||||
|
||||
t.imposm3_revert_deploy(t.db_conf, mapping_file)
|
||||
assert t.table_exists('osm_routes', schema=t.TEST_SCHEMA_IMPORT)
|
||||
assert t.table_exists('osm_routes', schema=t.TEST_SCHEMA_PRODUCTION)
|
||||
assert not t.table_exists('osm_routes', schema=t.TEST_SCHEMA_BACKUP)
|
||||
|
||||
def test_remove_backup():
|
||||
"""Remove backup succeeds"""
|
||||
assert t.table_exists('osm_routes', schema=t.TEST_SCHEMA_IMPORT)
|
||||
assert t.table_exists('osm_routes', schema=t.TEST_SCHEMA_PRODUCTION)
|
||||
assert not t.table_exists('osm_routes', schema=t.TEST_SCHEMA_BACKUP)
|
||||
|
||||
t.imposm3_deploy(t.db_conf, mapping_file)
|
||||
|
||||
assert not t.table_exists('osm_routes', schema=t.TEST_SCHEMA_IMPORT)
|
||||
assert t.table_exists('osm_routes', schema=t.TEST_SCHEMA_PRODUCTION)
|
||||
assert t.table_exists('osm_routes', schema=t.TEST_SCHEMA_BACKUP)
|
||||
|
||||
t.imposm3_remove_backups(t.db_conf, mapping_file)
|
||||
|
||||
assert not t.table_exists('osm_routes', schema=t.TEST_SCHEMA_IMPORT)
|
||||
assert t.table_exists('osm_routes', schema=t.TEST_SCHEMA_PRODUCTION)
|
||||
assert not t.table_exists('osm_routes', schema=t.TEST_SCHEMA_BACKUP)
|
||||
|
|
@ -9,17 +9,19 @@ import (
|
|||
"github.com/omniscale/imposm3/element"
|
||||
"github.com/omniscale/imposm3/expire"
|
||||
geomp "github.com/omniscale/imposm3/geom"
|
||||
"github.com/omniscale/imposm3/geom/geos"
|
||||
geosp "github.com/omniscale/imposm3/geom/geos"
|
||||
"github.com/omniscale/imposm3/mapping"
|
||||
"github.com/omniscale/imposm3/stats"
|
||||
)
|
||||
|
||||
type RelationWriter struct {
|
||||
OsmElemWriter
|
||||
singleIdSpace bool
|
||||
rel chan *element.Relation
|
||||
polygonMatcher mapping.RelWayMatcher
|
||||
maxGap float64
|
||||
singleIdSpace bool
|
||||
rel chan *element.Relation
|
||||
polygonMatcher mapping.RelWayMatcher
|
||||
relationMatcher mapping.RelationMatcher
|
||||
relationMemberMatcher mapping.RelationMatcher
|
||||
maxGap float64
|
||||
}
|
||||
|
||||
func NewRelationWriter(
|
||||
|
@ -30,6 +32,8 @@ func NewRelationWriter(
|
|||
inserter database.Inserter,
|
||||
progress *stats.Statistics,
|
||||
matcher mapping.RelWayMatcher,
|
||||
relMatcher mapping.RelationMatcher,
|
||||
relMemberMatcher mapping.RelationMatcher,
|
||||
srid int,
|
||||
) *OsmElemWriter {
|
||||
maxGap := 1e-1 // 0.1m
|
||||
|
@ -45,10 +49,12 @@ func NewRelationWriter(
|
|||
inserter: inserter,
|
||||
srid: srid,
|
||||
},
|
||||
singleIdSpace: singleIdSpace,
|
||||
polygonMatcher: matcher,
|
||||
rel: rel,
|
||||
maxGap: maxGap,
|
||||
singleIdSpace: singleIdSpace,
|
||||
polygonMatcher: matcher,
|
||||
relationMatcher: relMatcher,
|
||||
relationMemberMatcher: relMemberMatcher,
|
||||
rel: rel,
|
||||
maxGap: maxGap,
|
||||
}
|
||||
rw.OsmElemWriter.writer = &rw
|
||||
return &rw.OsmElemWriter
|
||||
|
@ -62,7 +68,7 @@ func (rw *RelationWriter) relId(id int64) int64 {
|
|||
}
|
||||
|
||||
func (rw *RelationWriter) loop() {
|
||||
geos := geos.NewGeos()
|
||||
geos := geosp.NewGeos()
|
||||
geos.SetHandleSrid(rw.srid)
|
||||
defer geos.Finish()
|
||||
|
||||
|
@ -76,7 +82,7 @@ NextRel:
|
|||
}
|
||||
continue NextRel
|
||||
}
|
||||
for _, m := range r.Members {
|
||||
for i, m := range r.Members {
|
||||
if m.Way == nil {
|
||||
continue
|
||||
}
|
||||
|
@ -88,6 +94,76 @@ NextRel:
|
|||
continue NextRel
|
||||
}
|
||||
rw.NodesToSrid(m.Way.Nodes)
|
||||
r.Members[i].Elem = &m.Way.OSMElem
|
||||
}
|
||||
|
||||
relMemberMatches := rw.relationMemberMatcher.MatchRelation(r)
|
||||
if len(relMemberMatches) > 0 {
|
||||
for i, m := range r.Members {
|
||||
if m.Type == element.RELATION {
|
||||
mrel, err := rw.osmCache.Relations.GetRelation(m.Id)
|
||||
if err != nil {
|
||||
if err == cache.NotFound {
|
||||
log.Warn(err)
|
||||
continue NextRel
|
||||
}
|
||||
}
|
||||
r.Members[i].Elem = &mrel.OSMElem
|
||||
} else if m.Type == element.NODE {
|
||||
nd, err := rw.osmCache.Nodes.GetNode(m.Id)
|
||||
if err != nil {
|
||||
if err == cache.NotFound {
|
||||
nd, err = rw.osmCache.Coords.GetCoord(m.Id)
|
||||
if err != nil {
|
||||
if err != cache.NotFound {
|
||||
log.Warn(err)
|
||||
}
|
||||
continue NextRel
|
||||
}
|
||||
} else {
|
||||
log.Warn(err)
|
||||
continue NextRel
|
||||
}
|
||||
}
|
||||
rw.NodeToSrid(nd)
|
||||
r.Members[i].Node = nd
|
||||
r.Members[i].Elem = &nd.OSMElem
|
||||
}
|
||||
}
|
||||
|
||||
for _, m := range r.Members {
|
||||
var g *geosp.Geom
|
||||
var err error
|
||||
if m.Node != nil {
|
||||
g, err = geomp.Point(geos, *m.Node)
|
||||
} else if m.Way != nil {
|
||||
g, err = geomp.LineString(geos, m.Way.Nodes)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
log.Warn(err)
|
||||
continue
|
||||
}
|
||||
|
||||
var gelem geomp.Geometry
|
||||
if g == nil {
|
||||
g = geos.FromWkt("POLYGON EMPTY")
|
||||
gelem = geomp.Geometry{Geom: g, Wkb: geos.AsEwkbHex(g)}
|
||||
} else {
|
||||
gelem, err = geomp.AsGeomElement(geos, g)
|
||||
if err != nil {
|
||||
log.Warn(err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
rw.inserter.InsertRelationMember(*r, m, gelem, relMemberMatches)
|
||||
}
|
||||
}
|
||||
|
||||
relMatches := rw.relationMatcher.MatchRelation(r)
|
||||
if len(relMatches) > 0 {
|
||||
rw.inserter.InsertPolygon(r.OSMElem, geomp.Geometry{}, relMatches)
|
||||
}
|
||||
|
||||
// BuildRelation updates r.Members but we need all of them
|
||||
|
|
Loading…
Reference in New Issue