diff --git a/database/database.go b/database/database.go index db08152..c265fbc 100644 --- a/database/database.go +++ b/database/database.go @@ -2,9 +2,11 @@ package database import ( "errors" - "github.com/omniscale/imposm3/element" - "github.com/omniscale/imposm3/mapping" "strings" + + "github.com/omniscale/imposm3/element" + "github.com/omniscale/imposm3/geom" + "github.com/omniscale/imposm3/mapping" ) type Config struct { @@ -31,9 +33,9 @@ type BulkBeginner interface { type Inserter interface { // InsertXxx inserts element of that type into the database. // element.Geom is set to that type. - InsertPoint(element.OSMElem, []mapping.Match) error - InsertLineString(element.OSMElem, []mapping.Match) error - InsertPolygon(element.OSMElem, []mapping.Match) error + InsertPoint(element.OSMElem, geom.Geometry, []mapping.Match) error + InsertLineString(element.OSMElem, geom.Geometry, []mapping.Match) error + InsertPolygon(element.OSMElem, geom.Geometry, []mapping.Match) error } type Deployer interface { @@ -93,14 +95,14 @@ func Open(conf Config, m *mapping.Mapping) (DB, error) { // nullDb is a dummy database that imports into /dev/null type nullDb struct{} -func (n *nullDb) Init() error { return nil } -func (n *nullDb) Begin() error { return nil } -func (n *nullDb) End() error { return nil } -func (n *nullDb) Close() error { return nil } -func (n *nullDb) Abort() error { return nil } -func (n *nullDb) InsertPoint(element.OSMElem, []mapping.Match) error { return nil } -func (n *nullDb) InsertLineString(element.OSMElem, []mapping.Match) error { return nil } -func (n *nullDb) InsertPolygon(element.OSMElem, []mapping.Match) error { return nil } +func (n *nullDb) Init() error { return nil } +func (n *nullDb) Begin() error { return nil } +func (n *nullDb) End() error { return nil } +func (n *nullDb) Close() error { return nil } +func (n *nullDb) Abort() error { return nil } +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 newNullDb(conf Config, m *mapping.Mapping) (DB, error) { return &nullDb{}, nil diff --git a/database/postgis/postgis.go b/database/postgis/postgis.go index e30d42e..8f15d2f 100644 --- a/database/postgis/postgis.go +++ b/database/postgis/postgis.go @@ -10,6 +10,7 @@ import ( pq "github.com/lib/pq" "github.com/omniscale/imposm3/database" "github.com/omniscale/imposm3/element" + "github.com/omniscale/imposm3/geom" "github.com/omniscale/imposm3/logging" "github.com/omniscale/imposm3/mapping" ) @@ -422,9 +423,9 @@ func (pg *PostGIS) Open() error { return nil } -func (pg *PostGIS) InsertPoint(elem element.OSMElem, matches []mapping.Match) error { +func (pg *PostGIS) InsertPoint(elem element.OSMElem, geom geom.Geometry, matches []mapping.Match) error { for _, match := range matches { - row := match.Row(&elem) + row := match.Row(&elem, &geom) if err := pg.txRouter.Insert(match.Table.Name, row); err != nil { return err } @@ -432,9 +433,9 @@ func (pg *PostGIS) InsertPoint(elem element.OSMElem, matches []mapping.Match) er return nil } -func (pg *PostGIS) InsertLineString(elem element.OSMElem, matches []mapping.Match) error { +func (pg *PostGIS) InsertLineString(elem element.OSMElem, geom geom.Geometry, matches []mapping.Match) error { for _, match := range matches { - row := match.Row(&elem) + row := match.Row(&elem, &geom) if err := pg.txRouter.Insert(match.Table.Name, row); err != nil { return err } @@ -447,9 +448,9 @@ func (pg *PostGIS) InsertLineString(elem element.OSMElem, matches []mapping.Matc return nil } -func (pg *PostGIS) InsertPolygon(elem element.OSMElem, matches []mapping.Match) error { +func (pg *PostGIS) InsertPolygon(elem element.OSMElem, geom geom.Geometry, matches []mapping.Match) error { for _, match := range matches { - row := match.Row(&elem) + row := match.Row(&elem, &geom) if err := pg.txRouter.Insert(match.Table.Name, row); err != nil { return err } diff --git a/element/element.go b/element/element.go index f3efa81..0bc5fdf 100644 --- a/element/element.go +++ b/element/element.go @@ -4,8 +4,6 @@ import ( "fmt" "math" "sort" - - "github.com/omniscale/imposm3/geom/geos" ) type Tags map[string]string @@ -15,9 +13,8 @@ func (t *Tags) String() string { } type OSMElem struct { - Id int64 `json:"-"` - Tags Tags `json:"tags,omitempty"` - Geom *Geometry `json:"-"` + Id int64 `json:"-"` + Tags Tags `json:"tags,omitempty"` } type Node struct { @@ -32,11 +29,6 @@ type Way struct { Nodes []Node `json:"nodes,omitempty"` } -type Geometry struct { - Geom *geos.Geom - Wkb []byte -} - func (w *Way) IsClosed() bool { return len(w.Refs) >= 4 && w.Refs[0] == w.Refs[len(w.Refs)-1] } diff --git a/geom/geom.go b/geom/geom.go index 95d68eb..0208a61 100644 --- a/geom/geom.go +++ b/geom/geom.go @@ -2,9 +2,10 @@ package geom import ( "errors" + "math" + "github.com/omniscale/imposm3/element" "github.com/omniscale/imposm3/geom/geos" - "math" ) type GeomError struct { @@ -12,6 +13,11 @@ type GeomError struct { level int } +type Geometry struct { + Geom *geos.Geom + Wkb []byte +} + func (e *GeomError) Error() string { return e.message } @@ -130,13 +136,13 @@ func Polygon(g *geos.Geos, nodes []element.Node) (*geos.Geom, error) { return geom, nil } -func AsGeomElement(g *geos.Geos, geom *geos.Geom) (*element.Geometry, error) { +func AsGeomElement(g *geos.Geos, geom *geos.Geom) (Geometry, error) { wkb := g.AsEwkbHex(geom) if wkb == nil { - return nil, errors.New("could not create wkb") + return Geometry{}, errors.New("could not create wkb") } - return &element.Geometry{ + return Geometry{ Wkb: wkb, Geom: geom, }, nil diff --git a/geom/multipolygon.go b/geom/multipolygon.go index 073ad5c..dc75829 100644 --- a/geom/multipolygon.go +++ b/geom/multipolygon.go @@ -8,30 +8,42 @@ import ( "github.com/omniscale/imposm3/geom/geos" ) -type preparedRelation struct { +type PreparedRelation struct { rings []*Ring rel *element.Relation srid int } -func PrepareRelation(rel *element.Relation, srid int, maxRingGap float64) (*preparedRelation, error) { - rings, err := BuildRings(rel, maxRingGap) +// PrepareRelation is the first step in building a (multi-)polygon of a Relation. +// It builds rings from all ways and returns an error if there are unclosed rings. +// It also merges the Relation.Tags with the Tags of the outer way. +func PrepareRelation(rel *element.Relation, srid int, maxRingGap float64) (PreparedRelation, error) { + rings, err := buildRings(rel, maxRingGap) if err != nil { - return nil, err + return PreparedRelation{}, err } rel.Tags = relationTags(rel.Tags, rings[0].ways[0].Tags) - return &preparedRelation{rings, rel, srid}, nil + return PreparedRelation{rings, rel, srid}, nil } -func (prep *preparedRelation) Build() (*element.Relation, error) { - _, err := BuildRelGeometry(prep.rel, prep.rings, prep.srid) - if err != nil { - return nil, err - } - return prep.rel, nil +// Build creates the (multi)polygon Geometry of the Relation. +func (prep *PreparedRelation) Build() (Geometry, error) { + g := geos.NewGeos() + g.SetHandleSrid(prep.srid) + defer g.Finish() + geom, err := buildRelGeometry(g, prep.rel, prep.rings) + if err != nil { + return Geometry{}, err + } + + wkb := g.AsEwkbHex(geom) + if wkb == nil { + return Geometry{}, errors.New("unable to create WKB for relation") + } + return Geometry{Geom: geom, Wkb: wkb}, nil } func destroyRings(g *geos.Geos, rings []*Ring) { @@ -43,7 +55,7 @@ func destroyRings(g *geos.Geos, rings []*Ring) { } } -func BuildRings(rel *element.Relation, maxRingGap float64) ([]*Ring, error) { +func buildRings(rel *element.Relation, maxRingGap float64) ([]*Ring, error) { var rings []*Ring var incompleteRings []*Ring var completeRings []*Ring @@ -114,13 +126,9 @@ func (r SortableRingsDesc) Len() int { return len(r) } func (r SortableRingsDesc) Less(i, j int) bool { return r[i].area > r[j].area } func (r SortableRingsDesc) Swap(i, j int) { r[i], r[j] = r[j], r[i] } -// BuildRelGeometry builds the geometry of rel by creating a multipolygon of all rings. +// buildRelGeometry builds the geometry of rel by creating a multipolygon of all rings. // rings need to be sorted by area (large to small). -func BuildRelGeometry(rel *element.Relation, rings []*Ring, srid int) (*geos.Geom, error) { - g := geos.NewGeos() - g.SetHandleSrid(srid) - defer g.Finish() - +func buildRelGeometry(g *geos.Geos, rel *element.Relation, rings []*Ring) (*geos.Geom, error) { totalRings := len(rings) shells := map[*Ring]bool{rings[0]: true} for i := 0; i < totalRings; i++ { @@ -216,12 +224,6 @@ func BuildRelGeometry(rel *element.Relation, rings []*Ring, srid int) (*geos.Geo } } - wkb := g.AsEwkbHex(result) - if wkb == nil { - return nil, errors.New("unable to create WKB for relation") - } - rel.Geom = &element.Geometry{Geom: result, Wkb: wkb} - return result, nil } diff --git a/geom/multipolygon_test.go b/geom/multipolygon_test.go index 2923505..fae9b14 100644 --- a/geom/multipolygon_test.go +++ b/geom/multipolygon_test.go @@ -26,14 +26,13 @@ func makeWay(id int64, tags element.Tags, coords []coord) element.Way { return way } -func buildRelation(rel *element.Relation, srid int) error { +func buildRelation(rel *element.Relation, srid int) (Geometry, error) { prep, err := PrepareRelation(rel, srid, 0.1) if err != nil { - return err + return Geometry{}, err } - _, err = prep.Build() - return err + return prep.Build() } func TestSimplePolygonWithHole(t *testing.T) { @@ -59,7 +58,10 @@ func TestSimplePolygonWithHole(t *testing.T) { {2, element.WAY, "inner", &w2}, } - buildRelation(&rel, 3857) + geom, err := buildRelation(&rel, 3857) + if err != nil { + t.Fatal(err) + } g := geos.NewGeos() defer g.Finish() @@ -67,11 +69,11 @@ func TestSimplePolygonWithHole(t *testing.T) { t.Fatal("wrong rel tags", rel.Tags) } - if !g.IsValid(rel.Geom.Geom) { - t.Fatal("geometry not valid", g.AsWkt(rel.Geom.Geom)) + if !g.IsValid(geom.Geom) { + t.Fatal("geometry not valid", g.AsWkt(geom.Geom)) } - if area := rel.Geom.Geom.Area(); area != 100-36 { + if area := geom.Geom.Area(); area != 100-36 { t.Fatal("area invalid", area) } } @@ -99,7 +101,10 @@ func TestMultiPolygonWithHoleAndRelName(t *testing.T) { {2, element.WAY, "inner", &w2}, } - buildRelation(&rel, 3857) + geom, err := buildRelation(&rel, 3857) + if err != nil { + t.Fatal(err) + } g := geos.NewGeos() defer g.Finish() @@ -110,11 +115,11 @@ func TestMultiPolygonWithHoleAndRelName(t *testing.T) { t.Fatal("wrong rel tags", rel.Tags) } - if !g.IsValid(rel.Geom.Geom) { - t.Fatal("geometry not valid", g.AsWkt(rel.Geom.Geom)) + if !g.IsValid(geom.Geom) { + t.Fatal("geometry not valid", g.AsWkt(geom.Geom)) } - if area := rel.Geom.Geom.Area(); area != 64 { + if area := geom.Geom.Area(); area != 64 { t.Fatal("aread not 64", area) } } @@ -150,7 +155,10 @@ func TestMultiPolygonWithMultipleHoles(t *testing.T) { {3, element.WAY, "inner", &w3}, } - buildRelation(&rel, 3857) + geom, err := buildRelation(&rel, 3857) + if err != nil { + t.Fatal(err) + } g := geos.NewGeos() defer g.Finish() @@ -161,11 +169,11 @@ func TestMultiPolygonWithMultipleHoles(t *testing.T) { t.Fatal("wrong rel tags", rel.Tags) } - if !g.IsValid(rel.Geom.Geom) { - t.Fatal("geometry not valid", g.AsWkt(rel.Geom.Geom)) + if !g.IsValid(geom.Geom) { + t.Fatal("geometry not valid", g.AsWkt(geom.Geom)) } - if area := rel.Geom.Geom.Area(); area != 100-1-1 { + if area := geom.Geom.Area(); area != 100-1-1 { t.Fatal("area invalid", area) } } @@ -216,7 +224,10 @@ func TestMultiPolygonWithNeastedHoles(t *testing.T) { {5, element.WAY, "inner", &w5}, } - buildRelation(&rel, 3857) + geom, err := buildRelation(&rel, 3857) + if err != nil { + t.Fatal(err) + } g := geos.NewGeos() defer g.Finish() @@ -227,11 +238,11 @@ func TestMultiPolygonWithNeastedHoles(t *testing.T) { t.Fatal("wrong rel tags", rel.Tags) } - if !g.IsValid(rel.Geom.Geom) { - t.Fatal("geometry not valid", g.AsWkt(rel.Geom.Geom)) + if !g.IsValid(geom.Geom) { + t.Fatal("geometry not valid", g.AsWkt(geom.Geom)) } - if area := rel.Geom.Geom.Area(); area != 100-64+36-16+4 { + if area := geom.Geom.Area(); area != 100-64+36-16+4 { t.Fatal("area invalid", area) } } @@ -258,7 +269,10 @@ func TestPolygonFromThreeWays(t *testing.T) { {3, element.WAY, "inner", &w3}, } - buildRelation(&rel, 3857) + geom, err := buildRelation(&rel, 3857) + if err != nil { + t.Fatal(err) + } g := geos.NewGeos() defer g.Finish() @@ -269,11 +283,11 @@ func TestPolygonFromThreeWays(t *testing.T) { t.Fatal("wrong rel tags", rel.Tags) } - if !g.IsValid(rel.Geom.Geom) { - t.Fatal("geometry not valid", g.AsWkt(rel.Geom.Geom)) + if !g.IsValid(geom.Geom) { + t.Fatal("geometry not valid", g.AsWkt(geom.Geom)) } - if area := rel.Geom.Geom.Area(); area != 100 { + if area := geom.Geom.Area(); area != 100 { t.Fatal("area invalid", area) } } @@ -307,7 +321,10 @@ func TestTouchingPolygonsWithHole(t *testing.T) { {2, element.WAY, "outer", &w2}, {3, element.WAY, "inner", &w3}, } - buildRelation(&rel, 3857) + geom, err := buildRelation(&rel, 3857) + if err != nil { + t.Fatal(err) + } g := geos.NewGeos() defer g.Finish() @@ -319,11 +336,11 @@ func TestTouchingPolygonsWithHole(t *testing.T) { t.Fatal("wrong rel tags", rel.Tags) } - if !g.IsValid(rel.Geom.Geom) { - t.Fatal("geometry not valid", g.AsWkt(rel.Geom.Geom)) + if !g.IsValid(geom.Geom) { + t.Fatal("geometry not valid", g.AsWkt(geom.Geom)) } - if area := rel.Geom.Geom.Area(); area != 100+200-36 { + if area := geom.Geom.Area(); area != 100+200-36 { t.Fatal("area invalid", area) } } @@ -346,7 +363,10 @@ func TestInsertedWaysDifferentTags(t *testing.T) { {2, element.WAY, "inner", &w2}, } - buildRelation(&rel, 3857) + geom, err := buildRelation(&rel, 3857) + if err != nil { + t.Fatal(err) + } g := geos.NewGeos() defer g.Finish() @@ -358,11 +378,11 @@ func TestInsertedWaysDifferentTags(t *testing.T) { t.Fatal("wrong rel tags", rel.Tags) } - if !g.IsValid(rel.Geom.Geom) { - t.Fatal("geometry not valid", g.AsWkt(rel.Geom.Geom)) + if !g.IsValid(geom.Geom) { + t.Fatal("geometry not valid", g.AsWkt(geom.Geom)) } - if area := rel.Geom.Geom.Area(); area != 100 { + if area := geom.Geom.Area(); area != 100 { t.Fatal("area invalid", area) } } @@ -385,7 +405,10 @@ func TestInsertMultipleTags(t *testing.T) { {2, element.WAY, "inner", &w2}, } - buildRelation(&rel, 3857) + geom, err := buildRelation(&rel, 3857) + if err != nil { + t.Fatal(err) + } g := geos.NewGeos() defer g.Finish() @@ -393,11 +416,11 @@ func TestInsertMultipleTags(t *testing.T) { t.Fatal("wrong rel tags", rel.Tags) } - if !g.IsValid(rel.Geom.Geom) { - t.Fatal("geometry not valid", g.AsWkt(rel.Geom.Geom)) + if !g.IsValid(geom.Geom) { + t.Fatal("geometry not valid", g.AsWkt(geom.Geom)) } - if area := rel.Geom.Geom.Area(); area != 100 { + if area := geom.Geom.Area(); area != 100 { t.Fatal("area invalid", area) } } @@ -431,7 +454,7 @@ func TestBrokenPolygonSelfIntersect(t *testing.T) { {2, element.WAY, "inner", &w2}, } - err := buildRelation(&rel1, 3857) + geom1, err := buildRelation(&rel1, 3857) if err != nil { t.Fatal(err) } @@ -442,11 +465,11 @@ func TestBrokenPolygonSelfIntersect(t *testing.T) { t.Fatal("wrong rel tags", rel1.Tags) } - if !g.IsValid(rel1.Geom.Geom) { - t.Fatal("geometry not valid", g.AsWkt(rel1.Geom.Geom)) + if !g.IsValid(geom1.Geom) { + t.Fatal("geometry not valid", g.AsWkt(geom1.Geom)) } - if area := rel1.Geom.Geom.Area(); area != 200-36 { + if area := geom1.Geom.Area(); area != 200-36 { t.Fatal("area invalid", area) } @@ -472,7 +495,7 @@ func TestBrokenPolygonSelfIntersect(t *testing.T) { {2, element.WAY, "inner", &w2}, } - err = buildRelation(&rel2, 3857) + geom2, err := buildRelation(&rel2, 3857) if err != nil { t.Fatal(err) } @@ -484,11 +507,11 @@ func TestBrokenPolygonSelfIntersect(t *testing.T) { t.Fatal("wrong rel tags", rel2.Tags) } - if !g.IsValid(rel2.Geom.Geom) { - t.Fatal("geometry not valid", g.AsWkt(rel2.Geom.Geom)) + if !g.IsValid(geom2.Geom) { + t.Fatal("geometry not valid", g.AsWkt(geom2.Geom)) } - if area := rel2.Geom.Geom.Area(); area != 200-36 { + if area := geom2.Geom.Area(); area != 200-36 { t.Fatal("area invalid", area) } } @@ -521,7 +544,7 @@ func TestBrokenPolygonSelfIntersectTriangle(t *testing.T) { {2, element.WAY, "inner", &w2}, } - err := buildRelation(&rel, 3857) + geom, err := buildRelation(&rel, 3857) if err != nil { t.Fatal(err) } @@ -529,11 +552,11 @@ func TestBrokenPolygonSelfIntersectTriangle(t *testing.T) { g := geos.NewGeos() defer g.Finish() - if !g.IsValid(rel.Geom.Geom) { - t.Fatal("geometry not valid", g.AsWkt(rel.Geom.Geom)) + if !g.IsValid(geom.Geom) { + t.Fatal("geometry not valid", g.AsWkt(geom.Geom)) } - area := rel.Geom.Geom.Area() + area := geom.Geom.Area() // as for python assertAlmostEqual(a, b) round(a-b, 7) == 0 if math.Abs(area-(100*100/2-100)) > 0.01 { t.Fatal("area invalid", area) @@ -561,16 +584,16 @@ func TestBrokenPolygonSelfIntersectTriangle(t *testing.T) { {2, element.WAY, "inner", &w4}, } - err = buildRelation(&rel, 3857) + geom, err = buildRelation(&rel, 3857) if err != nil { t.Fatal(err) } - if !g.IsValid(rel.Geom.Geom) { - t.Fatal("geometry not valid", g.AsWkt(rel.Geom.Geom)) + if !g.IsValid(geom.Geom) { + t.Fatal("geometry not valid", g.AsWkt(geom.Geom)) } - area = rel.Geom.Geom.Area() + area = geom.Geom.Area() if math.Abs((area - (100*98/2 - 100))) > 10 { t.Fatal("area invalid", area) diff --git a/mapping/fields.go b/mapping/fields.go index d6d12c0..21436a7 100644 --- a/mapping/fields.go +++ b/mapping/fields.go @@ -7,6 +7,7 @@ import ( "strings" "github.com/omniscale/imposm3/element" + "github.com/omniscale/imposm3/geom" "github.com/omniscale/imposm3/logging" ) @@ -34,7 +35,7 @@ func init() { } } -type MakeValue func(string, *element.OSMElem, Match) interface{} +type MakeValue func(string, *element.OSMElem, *geom.Geometry, Match) interface{} type MakeMakeValue func(string, FieldType, Field) (MakeValue, error) @@ -46,9 +47,9 @@ type FieldSpec struct { Type FieldType } -func (f *FieldSpec) Value(elem *element.OSMElem, match Match) interface{} { +func (f *FieldSpec) Value(elem *element.OSMElem, geom *geom.Geometry, match Match) interface{} { if f.Type.Func != nil { - return f.Type.Func(elem.Tags[string(f.Key)], elem, match) + return f.Type.Func(elem.Tags[string(f.Key)], elem, geom, match) } return nil } @@ -57,10 +58,10 @@ type TableFields struct { fields []FieldSpec } -func (t *TableFields) MakeRow(elem *element.OSMElem, match Match) []interface{} { +func (t *TableFields) MakeRow(elem *element.OSMElem, geom *geom.Geometry, match Match) []interface{} { var row []interface{} for _, field := range t.fields { - row = append(row, field.Value(elem, match)) + row = append(row, field.Value(elem, geom, match)) } return row } @@ -105,25 +106,25 @@ type FieldType struct { MakeFunc MakeMakeValue } -func Bool(val string, elem *element.OSMElem, match Match) interface{} { +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, match Match) interface{} { +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, match Match) interface{} { +func String(val string, elem *element.OSMElem, geom *geom.Geometry, match Match) interface{} { return val } -func Integer(val string, elem *element.OSMElem, match Match) interface{} { +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 @@ -131,19 +132,19 @@ func Integer(val string, elem *element.OSMElem, match Match) interface{} { return v } -func Id(val string, elem *element.OSMElem, match Match) interface{} { +func Id(val string, elem *element.OSMElem, geom *geom.Geometry, match Match) interface{} { return elem.Id } -func KeyName(val string, elem *element.OSMElem, match Match) interface{} { +func KeyName(val string, elem *element.OSMElem, geom *geom.Geometry, match Match) interface{} { return match.Key } -func ValueName(val string, elem *element.OSMElem, match Match) interface{} { +func ValueName(val string, elem *element.OSMElem, geom *geom.Geometry, match Match) interface{} { return match.Value } -func Direction(val string, elem *element.OSMElem, match Match) interface{} { +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" { @@ -153,12 +154,12 @@ func Direction(val string, elem *element.OSMElem, match Match) interface{} { } } -func Geometry(val string, elem *element.OSMElem, match Match) interface{} { - return string(elem.Geom.Wkb) +func Geometry(val string, elem *element.OSMElem, geom *geom.Geometry, match Match) interface{} { + return string(geom.Wkb) } -func PseudoArea(val string, elem *element.OSMElem, match Match) interface{} { - area := elem.Geom.Geom.Area() +func PseudoArea(val string, elem *element.OSMElem, geom *geom.Geometry, match Match) interface{} { + area := geom.Geom.Area() if area == 0.0 { return nil } @@ -167,7 +168,7 @@ func PseudoArea(val string, elem *element.OSMElem, match Match) interface{} { var hstoreReplacer = strings.NewReplacer("\\", "\\\\", "\"", "\\\"") -func HstoreString(val string, elem *element.OSMElem, match Match) interface{} { +func HstoreString(val string, elem *element.OSMElem, geom *geom.Geometry, match Match) interface{} { tags := make([]string, 0, len(elem.Tags)) for k, v := range elem.Tags { tags = append(tags, `"`+hstoreReplacer.Replace(k)+`"=>"`+hstoreReplacer.Replace(v)+`"`) @@ -196,7 +197,7 @@ func init() { } } -func WayZOrder(val string, elem *element.OSMElem, match Match) interface{} { +func WayZOrder(val string, elem *element.OSMElem, geom *geom.Geometry, match Match) interface{} { var z int32 layer, _ := strconv.ParseInt(elem.Tags["layer"], 10, 64) z += int32(layer) * 10 @@ -252,7 +253,7 @@ func MakeZOrder(fieldName string, fieldType FieldType, field Field) (MakeValue, ranks[rankName] = len(rankList) - i } - zOrder := func(val string, elem *element.OSMElem, match Match) interface{} { + 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 @@ -290,7 +291,7 @@ func MakeSuffixReplace(fieldName string, fieldType FieldType, field Field) (Make return changes[match].(string) } - suffixReplace := func(val string, elem *element.OSMElem, match Match) interface{} { + suffixReplace := func(val string, elem *element.OSMElem, geom *geom.Geometry, match Match) interface{} { if val != "" { return re.ReplaceAllStringFunc(val, replFunc) } diff --git a/mapping/fields_test.go b/mapping/fields_test.go index 444d532..aff33ed 100644 --- a/mapping/fields_test.go +++ b/mapping/fields_test.go @@ -8,31 +8,31 @@ import ( func TestBool(t *testing.T) { match := Match{} - if false != Bool("", nil, match) { + if false != Bool("", nil, nil, match) { t.Fatal() } - if false != Bool("false", nil, match) { + if false != Bool("false", nil, nil, match) { t.Fatal() } - if false != Bool("no", nil, match) { + if false != Bool("no", nil, nil, match) { t.Fatal() } - if false != Bool("0", nil, match) { + if false != Bool("0", nil, nil, match) { t.Fatal() } - if true != Bool("yes", nil, match) { + if true != Bool("yes", nil, nil, match) { t.Fatal() } - if true != Bool("1", nil, match) { + if true != Bool("1", nil, nil, match) { t.Fatal() } - if true != Bool("true", nil, match) { + if true != Bool("true", nil, nil, match) { t.Fatal() } // Bool defaults to true - if true != Bool("other", nil, match) { + if true != Bool("other", nil, nil, match) { t.Fatal() } @@ -40,30 +40,30 @@ func TestBool(t *testing.T) { func TestInteger(t *testing.T) { match := Match{} - if v := Integer("", nil, match); v != nil { + if v := Integer("", nil, nil, match); v != nil { t.Errorf(" -> %v", v) } - if v := Integer("bar", nil, match); v != nil { + if v := Integer("bar", nil, nil, match); v != nil { t.Errorf("bar -> %v", v) } - if v := Integer("1e6", nil, match); v != nil { + if v := Integer("1e6", nil, nil, match); v != nil { t.Errorf("1e6 -> %v", v) } - if v := Integer("0", nil, match); v.(int64) != 0 { + if v := Integer("0", nil, nil, match); v.(int64) != 0 { t.Errorf("0 -> %v", v) } - if v := Integer("123456", nil, match); v.(int64) != 123456 { + if v := Integer("123456", nil, nil, match); v.(int64) != 123456 { t.Errorf("123456 -> %v", v) } - if v := Integer("-123456", nil, match); v.(int64) != -123456 { + if v := Integer("-123456", nil, nil, match); v.(int64) != -123456 { t.Errorf("-123456 -> %v", v) } // >2^32, but <2^64, Integer type defaults to int32 - if v := Integer("1000000000000000000", nil, match); v != nil { + if v := Integer("1000000000000000000", nil, nil, match); v != nil { t.Errorf("1000000000000000000 -> %v", v) } // >2^64 - if v := Integer("19082139812039812093908123", nil, match); v != nil { + if v := Integer("19082139812039812093908123", nil, nil, match); v != nil { t.Errorf("19082139812039812093908123 -> %v", v) } } @@ -86,23 +86,23 @@ func TestZOrder(t *testing.T) { elem := &element.OSMElem{} elem.Tags = element.Tags{} // missing - if v := zOrder("", elem, match); v != 0 { + if v := zOrder("", elem, nil, match); v != 0 { t.Errorf(" -> %v", v) } elem.Tags = element.Tags{"fips": "ABCD"} // unknown - if v := zOrder("", elem, match); v != 0 { + if v := zOrder("", elem, nil, match); v != 0 { t.Errorf(" -> %v", v) } elem.Tags = element.Tags{"fips": "AA"} - if v := zOrder("", elem, match); v != 4 { + if v := zOrder("", elem, nil, match); v != 4 { t.Errorf(" -> %v", v) } elem.Tags = element.Tags{"fips": "CC"} - if v := zOrder("", elem, match); v != 3 { + if v := zOrder("", elem, nil, match); v != 3 { t.Errorf(" -> %v", v) } elem.Tags = element.Tags{"fips": "ZZ"} - if v := zOrder("", elem, match); v != 1 { + if v := zOrder("", elem, nil, match); v != 1 { t.Errorf(" -> %v", v) } } @@ -117,13 +117,13 @@ func TestMakeSuffixReplace(t *testing.T) { t.Fatal(err) } - if result := suffixReplace("Hauptstraße", nil, Match{}); result != "Hauptstr." { + if result := suffixReplace("Hauptstraße", nil, nil, Match{}); result != "Hauptstr." { t.Fatal(result) } - if result := suffixReplace("", nil, Match{}); result != "" { + if result := suffixReplace("", nil, nil, Match{}); result != "" { t.Fatal(result) } - if result := suffixReplace("Foostraßeee", nil, Match{}); result != "Foostraßeee" { + if result := suffixReplace("Foostraßeee", nil, nil, Match{}); result != "Foostraßeee" { t.Fatal(result) } } @@ -136,8 +136,8 @@ func assertEq(t *testing.T, a, b string) { func TestHstoreString(t *testing.T) { match := Match{} - assertEq(t, HstoreString("", &element.OSMElem{Tags: element.Tags{"key": "value"}}, match).(string), `"key"=>"value"`) - assertEq(t, HstoreString("", &element.OSMElem{Tags: element.Tags{`"key"`: `'"value"'`}}, match).(string), `"\"key\""=>"'\"value\"'"`) - assertEq(t, HstoreString("", &element.OSMElem{Tags: element.Tags{`\`: `\\\\`}}, match).(string), `"\\"=>"\\\\\\\\"`) - assertEq(t, HstoreString("", &element.OSMElem{Tags: element.Tags{"Ümlåütê=>": ""}}, match).(string), `"Ümlåütê=>"=>""`) + assertEq(t, HstoreString("", &element.OSMElem{Tags: element.Tags{"key": "value"}}, nil, match).(string), `"key"=>"value"`) + assertEq(t, HstoreString("", &element.OSMElem{Tags: element.Tags{`"key"`: `'"value"'`}}, nil, match).(string), `"\"key\""=>"'\"value\"'"`) + assertEq(t, HstoreString("", &element.OSMElem{Tags: element.Tags{`\`: `\\\\`}}, nil, match).(string), `"\\"=>"\\\\\\\\"`) + assertEq(t, HstoreString("", &element.OSMElem{Tags: element.Tags{"Ümlåütê=>": ""}}, nil, match).(string), `"Ümlåütê=>"=>""`) } diff --git a/mapping/matcher.go b/mapping/matcher.go index ab94559..0f686ec 100644 --- a/mapping/matcher.go +++ b/mapping/matcher.go @@ -1,6 +1,9 @@ package mapping -import "github.com/omniscale/imposm3/element" +import ( + "github.com/omniscale/imposm3/element" + "github.com/omniscale/imposm3/geom" +) func (m *Mapping) PointMatcher() NodeMatcher { mappings := make(TagTables) @@ -53,8 +56,8 @@ type tagMatcher struct { matchAreas bool } -func (m *Match) Row(elem *element.OSMElem) []interface{} { - return m.tableFields.MakeRow(elem, *m) +func (m *Match) Row(elem *element.OSMElem, geom *geom.Geometry) []interface{} { + return m.tableFields.MakeRow(elem, geom, *m) } func (tm *tagMatcher) MatchNode(node *element.Node) []Match { diff --git a/mapping/matcher_test.go b/mapping/matcher_test.go index 6600edd..b7d101d 100644 --- a/mapping/matcher_test.go +++ b/mapping/matcher_test.go @@ -23,7 +23,7 @@ func BenchmarkTagMatch(b *testing.B) { func makeMember(id int64, tags element.Tags) element.Member { way := &element.Way{ - element.OSMElem{id, tags, nil}, + element.OSMElem{id, tags}, []int64{0, 1, 2, 0}, // fake closed way, req. for SelectRelationPolygons nil} return element.Member{Id: id, Type: element.WAY, Role: "", Way: way} @@ -31,7 +31,7 @@ func makeMember(id int64, tags element.Tags) element.Member { func makeMemberRole(id int64, tags element.Tags, role string) element.Member { way := &element.Way{ - element.OSMElem{id, tags, nil}, + element.OSMElem{id, tags}, []int64{0, 1, 2, 0}, // fake closed way, req. for SelectRelationPolygons nil} return element.Member{Id: id, Type: element.WAY, Role: role, Way: way} diff --git a/writer/nodes.go b/writer/nodes.go index 94c67fa..da5e201 100644 --- a/writer/nodes.go +++ b/writer/nodes.go @@ -1,14 +1,15 @@ package writer import ( + "sync" + "github.com/omniscale/imposm3/cache" "github.com/omniscale/imposm3/database" "github.com/omniscale/imposm3/element" - "github.com/omniscale/imposm3/geom" + geomp "github.com/omniscale/imposm3/geom" "github.com/omniscale/imposm3/geom/geos" "github.com/omniscale/imposm3/mapping" "github.com/omniscale/imposm3/stats" - "sync" ) type NodeWriter struct { @@ -52,7 +53,7 @@ func (nw *NodeWriter) loop() { if nw.expireor != nil { nw.expireor.Expire(n.Long, n.Lat) } - point, err := geom.Point(geos, *n) + point, err := geomp.Point(geos, *n) if err != nil { if errl, ok := err.(ErrorLevel); !ok || errl.Level() > 0 { log.Warn(err) @@ -60,26 +61,26 @@ func (nw *NodeWriter) loop() { continue } - n.Geom, err = geom.AsGeomElement(geos, point) + geom, err := geomp.AsGeomElement(geos, point) if err != nil { log.Warn(err) continue } if nw.limiter != nil { - parts, err := nw.limiter.Clip(n.Geom.Geom) + parts, err := nw.limiter.Clip(geom.Geom) if err != nil { log.Warn(err) continue } if len(parts) >= 1 { - if err := nw.inserter.InsertPoint(n.OSMElem, matches); err != nil { + if err := nw.inserter.InsertPoint(n.OSMElem, geom, matches); err != nil { log.Warn(err) continue } } } else { - if err := nw.inserter.InsertPoint(n.OSMElem, matches); err != nil { + if err := nw.inserter.InsertPoint(n.OSMElem, geom, matches); err != nil { log.Warn(err) continue } diff --git a/writer/relations.go b/writer/relations.go index 1c5b5fb..5fad3b7 100644 --- a/writer/relations.go +++ b/writer/relations.go @@ -8,7 +8,7 @@ import ( "github.com/omniscale/imposm3/database" "github.com/omniscale/imposm3/element" "github.com/omniscale/imposm3/expire" - "github.com/omniscale/imposm3/geom" + geomp "github.com/omniscale/imposm3/geom" "github.com/omniscale/imposm3/geom/geos" "github.com/omniscale/imposm3/mapping" "github.com/omniscale/imposm3/stats" @@ -96,7 +96,7 @@ NextRel: // prepare relation first (build rings and compute actual // relation tags) - prepedRel, err := geom.PrepareRelation(r, rw.srid, rw.maxGap) + prepedRel, err := geomp.PrepareRelation(r, rw.srid, rw.maxGap) if err != nil { if errl, ok := err.(ErrorLevel); !ok || errl.Level() > 0 { log.Warn(err) @@ -111,10 +111,10 @@ NextRel: } // build the multipolygon - r, err = prepedRel.Build() + geom, err := prepedRel.Build() if err != nil { - if r.Geom != nil && r.Geom.Geom != nil { - geos.Destroy(r.Geom.Geom) + if geom.Geom != nil { + geos.Destroy(geom.Geom) } if errl, ok := err.(ErrorLevel); !ok || errl.Level() > 0 { log.Warn(err) @@ -124,7 +124,7 @@ NextRel: if rw.limiter != nil { start := time.Now() - parts, err := rw.limiter.Clip(r.Geom.Geom) + parts, err := rw.limiter.Clip(geom.Geom) if err != nil { log.Warn(err) continue NextRel @@ -135,8 +135,8 @@ NextRel: for _, g := range parts { rel := element.Relation(*r) rel.Id = rw.relId(r.Id) - rel.Geom = &element.Geometry{Geom: g, Wkb: geos.AsEwkbHex(g)} - err := rw.inserter.InsertPolygon(rel.OSMElem, matches) + geom = geomp.Geometry{Geom: g, Wkb: geos.AsEwkbHex(g)} + err := rw.inserter.InsertPolygon(rel.OSMElem, geom, matches) if err != nil { if errl, ok := err.(ErrorLevel); !ok || errl.Level() > 0 { log.Warn(err) @@ -147,7 +147,7 @@ NextRel: } else { rel := element.Relation(*r) rel.Id = rw.relId(r.Id) - err := rw.inserter.InsertPolygon(rel.OSMElem, matches) + err := rw.inserter.InsertPolygon(rel.OSMElem, geom, matches) if err != nil { if errl, ok := err.(ErrorLevel); !ok || errl.Level() > 0 { log.Warn(err) @@ -177,7 +177,7 @@ NextRel: } } } - geos.Destroy(r.Geom.Geom) + geos.Destroy(geom.Geom) } rw.wg.Done() } diff --git a/writer/ways.go b/writer/ways.go index 5644d68..be0be00 100644 --- a/writer/ways.go +++ b/writer/ways.go @@ -7,7 +7,7 @@ import ( "github.com/omniscale/imposm3/database" "github.com/omniscale/imposm3/element" "github.com/omniscale/imposm3/expire" - "github.com/omniscale/imposm3/geom" + geomp "github.com/omniscale/imposm3/geom" "github.com/omniscale/imposm3/geom/geos" "github.com/omniscale/imposm3/mapping" "github.com/omniscale/imposm3/stats" @@ -128,44 +128,44 @@ func (ww *WayWriter) buildAndInsert(g *geos.Geos, w *element.Way, matches []mapp way := element.Way(*w) if isPolygon { - geosgeom, err = geom.Polygon(g, way.Nodes) + geosgeom, err = geomp.Polygon(g, way.Nodes) } else { - geosgeom, err = geom.LineString(g, way.Nodes) + geosgeom, err = geomp.LineString(g, way.Nodes) } if err != nil { return err } - way.Geom, err = geom.AsGeomElement(g, geosgeom) + geom, err := geomp.AsGeomElement(g, geosgeom) if err != nil { return err } if ww.limiter != nil { - parts, err := ww.limiter.Clip(way.Geom.Geom) + parts, err := ww.limiter.Clip(geom.Geom) if err != nil { return err } for _, p := range parts { way := element.Way(*w) - way.Geom = &element.Geometry{Geom: p, Wkb: g.AsEwkbHex(p)} + geom = geomp.Geometry{Geom: p, Wkb: g.AsEwkbHex(p)} if isPolygon { - if err := ww.inserter.InsertPolygon(way.OSMElem, matches); err != nil { + if err := ww.inserter.InsertPolygon(way.OSMElem, geom, matches); err != nil { return err } } else { - if err := ww.inserter.InsertLineString(way.OSMElem, matches); err != nil { + if err := ww.inserter.InsertLineString(way.OSMElem, geom, matches); err != nil { return err } } } } else { if isPolygon { - if err := ww.inserter.InsertPolygon(way.OSMElem, matches); err != nil { + if err := ww.inserter.InsertPolygon(way.OSMElem, geom, matches); err != nil { return err } } else { - if err := ww.inserter.InsertLineString(way.OSMElem, matches); err != nil { + if err := ww.inserter.InsertLineString(way.OSMElem, geom, matches); err != nil { return err } }