From 423390ea71fc40f659aac649b1fe0807473f7def Mon Sep 17 00:00:00 2001 From: Oliver Tonnhofer Date: Mon, 4 Jan 2016 11:19:28 +0100 Subject: [PATCH] Add new relation_members table type --- database/database.go | 4 + database/postgis/postgis.go | 16 +++- database/postgis/spec.go | 10 ++- diff/process.go | 2 + element/element.go | 2 + import_/import.go | 2 + mapping/config.go | 25 +++--- mapping/fields.go | 95 ++++++++++++++++++----- mapping/matcher.go | 34 ++++++-- test/Makefile | 10 ++- test/route_relation.osc | 3 + test/route_relation.osm | 113 +++++++++++++++++++++++++++ test/route_relation_mapping.json | 128 +++++++++++++++++++++++++++++++ test/route_relation_test.go | 53 +++++++++++++ test/route_relation_test.py | 83 ++++++++++++++++++++ writer/relations.go | 98 ++++++++++++++++++++--- 16 files changed, 626 insertions(+), 52 deletions(-) create mode 100644 test/route_relation.osc create mode 100644 test/route_relation.osm create mode 100644 test/route_relation_mapping.json create mode 100644 test/route_relation_test.go create mode 100644 test/route_relation_test.py diff --git a/database/database.go b/database/database.go index c265fbc..50a8fdf 100644 --- a/database/database.go +++ b/database/database.go @@ -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 diff --git a/database/postgis/postgis.go b/database/postgis/postgis.go index 3fec077..8c710ec 100644 --- a/database/postgis/postgis.go +++ b/database/postgis/postgis.go @@ -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 { diff --git a/database/postgis/spec.go b/database/postgis/spec.go index e6fa7f0..8c50428 100644 --- a/database/postgis/spec.go +++ b/database/postgis/spec.go @@ -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 { diff --git a/diff/process.go b/diff/process.go index ca378fa..c15da9a 100644 --- a/diff/process.go +++ b/diff/process.go @@ -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) diff --git a/element/element.go b/element/element.go index 0bc5fdf..c98f586 100644 --- a/element/element.go +++ b/element/element.go @@ -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 { diff --git a/import_/import.go b/import_/import.go index 584e1b5..7138c4f 100644 --- a/import_/import.go +++ b/import_/import.go @@ -182,6 +182,8 @@ func Import() { relations, db, progress, tagmapping.PolygonMatcher(), + tagmapping.RelationMatcher(), + tagmapping.RelationMemberMatcher(), config.BaseOptions.Srid) relWriter.SetLimiter(geometryLimiter) relWriter.EnableConcurrent() diff --git a/mapping/config.go b/mapping/config.go index 78de91d..430510c 100644 --- a/mapping/config.go +++ b/mapping/config.go @@ -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) { diff --git a/mapping/fields.go b/mapping/fields.go index c899ed0..15a7900 100644 --- a/mapping/fields.go +++ b/mapping/fields.go @@ -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 diff --git a/mapping/matcher.go b/mapping/matcher.go index 38926ae..ffab4ad 100644 --- a/mapping/matcher.go +++ b/mapping/matcher.go @@ -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) } diff --git a/test/Makefile b/test/Makefile index 516e27e..fa6e637 100644 --- a/test/Makefile +++ b/test/Makefile @@ -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)) diff --git a/test/route_relation.osc b/test/route_relation.osc new file mode 100644 index 0000000..7f21e40 --- /dev/null +++ b/test/route_relation.osc @@ -0,0 +1,3 @@ + + + diff --git a/test/route_relation.osm b/test/route_relation.osm new file mode 100644 index 0000000..b1d5682 --- /dev/null +++ b/test/route_relation.osm @@ -0,0 +1,113 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/route_relation_mapping.json b/test/route_relation_mapping.json new file mode 100644 index 0000000..c81fc92 --- /dev/null +++ b/test/route_relation_mapping.json @@ -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" + ] + } + } + } +} diff --git a/test/route_relation_test.go b/test/route_relation_test.go new file mode 100644 index 0000000..f71c0af --- /dev/null +++ b/test/route_relation_test.go @@ -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") +} diff --git a/test/route_relation_test.py b/test/route_relation_test.py new file mode 100644 index 0000000..eb20330 --- /dev/null +++ b/test/route_relation_test.py @@ -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) + diff --git a/writer/relations.go b/writer/relations.go index 5fad3b7..8351456 100644 --- a/writer/relations.go +++ b/writer/relations.go @@ -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