update relations when member node changed
parent
8eae74a6da
commit
766118b324
|
@ -1,7 +1,6 @@
|
||||||
package cache
|
package cache
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/jmhodges/levigo"
|
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
@ -9,6 +8,8 @@ import (
|
||||||
"sort"
|
"sort"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
"github.com/jmhodges/levigo"
|
||||||
|
|
||||||
"github.com/omniscale/imposm3/cache/binary"
|
"github.com/omniscale/imposm3/cache/binary"
|
||||||
"github.com/omniscale/imposm3/element"
|
"github.com/omniscale/imposm3/element"
|
||||||
)
|
)
|
||||||
|
@ -21,8 +22,9 @@ func (a byInt64) Less(i, j int) bool { return a[i] < a[j] }
|
||||||
|
|
||||||
type DiffCache struct {
|
type DiffCache struct {
|
||||||
Dir string
|
Dir string
|
||||||
Coords *CoordsRefIndex
|
Coords *CoordsRefIndex // Stores which ways a coord references
|
||||||
Ways *WaysRefIndex
|
CoordsRel *CoordsRelRefIndex // Stores which relations a coord references
|
||||||
|
Ways *WaysRefIndex // Stores which relations a way references
|
||||||
opened bool
|
opened bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,6 +38,10 @@ func (c *DiffCache) Close() {
|
||||||
c.Coords.Close()
|
c.Coords.Close()
|
||||||
c.Coords = nil
|
c.Coords = nil
|
||||||
}
|
}
|
||||||
|
if c.CoordsRel != nil {
|
||||||
|
c.CoordsRel.Close()
|
||||||
|
c.CoordsRel = nil
|
||||||
|
}
|
||||||
if c.Ways != nil {
|
if c.Ways != nil {
|
||||||
c.Ways.Close()
|
c.Ways.Close()
|
||||||
c.Ways = nil
|
c.Ways = nil
|
||||||
|
@ -46,6 +52,9 @@ func (c *DiffCache) Flush() {
|
||||||
if c.Coords != nil {
|
if c.Coords != nil {
|
||||||
c.Coords.Flush()
|
c.Coords.Flush()
|
||||||
}
|
}
|
||||||
|
if c.CoordsRel != nil {
|
||||||
|
c.CoordsRel.Flush()
|
||||||
|
}
|
||||||
if c.Ways != nil {
|
if c.Ways != nil {
|
||||||
c.Ways.Flush()
|
c.Ways.Flush()
|
||||||
}
|
}
|
||||||
|
@ -58,6 +67,11 @@ func (c *DiffCache) Open() error {
|
||||||
c.Close()
|
c.Close()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
c.CoordsRel, err = newCoordsRelRefIndex(filepath.Join(c.Dir, "coords_rel_index"))
|
||||||
|
if err != nil {
|
||||||
|
c.Close()
|
||||||
|
return err
|
||||||
|
}
|
||||||
c.Ways, err = newWaysRefIndex(filepath.Join(c.Dir, "ways_index"))
|
c.Ways, err = newWaysRefIndex(filepath.Join(c.Dir, "ways_index"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Close()
|
c.Close()
|
||||||
|
@ -74,6 +88,9 @@ func (c *DiffCache) Exists() bool {
|
||||||
if _, err := os.Stat(filepath.Join(c.Dir, "coords_index")); !os.IsNotExist(err) {
|
if _, err := os.Stat(filepath.Join(c.Dir, "coords_index")); !os.IsNotExist(err) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
if _, err := os.Stat(filepath.Join(c.Dir, "coords_rel_index")); !os.IsNotExist(err) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
if _, err := os.Stat(filepath.Join(c.Dir, "ways_index")); !os.IsNotExist(err) {
|
if _, err := os.Stat(filepath.Join(c.Dir, "ways_index")); !os.IsNotExist(err) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -87,6 +104,9 @@ func (c *DiffCache) Remove() error {
|
||||||
if err := os.RemoveAll(filepath.Join(c.Dir, "coords_index")); err != nil {
|
if err := os.RemoveAll(filepath.Join(c.Dir, "coords_index")); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if err := os.RemoveAll(filepath.Join(c.Dir, "coords_rel_index")); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
if err := os.RemoveAll(filepath.Join(c.Dir, "ways_index")); err != nil {
|
if err := os.RemoveAll(filepath.Join(c.Dir, "ways_index")); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -198,6 +218,9 @@ func newRefIndex(path string, opts *cacheOptions) (*bunchRefCache, error) {
|
||||||
type CoordsRefIndex struct {
|
type CoordsRefIndex struct {
|
||||||
bunchRefCache
|
bunchRefCache
|
||||||
}
|
}
|
||||||
|
type CoordsRelRefIndex struct {
|
||||||
|
bunchRefCache
|
||||||
|
}
|
||||||
type WaysRefIndex struct {
|
type WaysRefIndex struct {
|
||||||
bunchRefCache
|
bunchRefCache
|
||||||
}
|
}
|
||||||
|
@ -210,6 +233,14 @@ func newCoordsRefIndex(dir string) (*CoordsRefIndex, error) {
|
||||||
return &CoordsRefIndex{*cache}, nil
|
return &CoordsRefIndex{*cache}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func newCoordsRelRefIndex(dir string) (*CoordsRelRefIndex, error) {
|
||||||
|
cache, err := newRefIndex(dir, &globalCacheOptions.CoordsIndex)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &CoordsRelRefIndex{*cache}, nil
|
||||||
|
}
|
||||||
|
|
||||||
func newWaysRefIndex(dir string) (*WaysRefIndex, error) {
|
func newWaysRefIndex(dir string) (*WaysRefIndex, error) {
|
||||||
cache, err := newRefIndex(dir, &globalCacheOptions.WaysIndex)
|
cache, err := newRefIndex(dir, &globalCacheOptions.WaysIndex)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -365,6 +396,18 @@ func (index *CoordsRefIndex) DeleteFromWay(way *element.Way) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (index *CoordsRelRefIndex) AddFromMembers(relId int64, members []element.Member) {
|
||||||
|
for _, member := range members {
|
||||||
|
if member.Type == element.NODE {
|
||||||
|
if index.linearImport {
|
||||||
|
index.addc <- idRef{id: member.Id, ref: relId}
|
||||||
|
} else {
|
||||||
|
index.Add(member.Id, relId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (index *WaysRefIndex) AddFromMembers(relId int64, members []element.Member) {
|
func (index *WaysRefIndex) AddFromMembers(relId int64, members []element.Member) {
|
||||||
for _, member := range members {
|
for _, member := range members {
|
||||||
if member.Type == element.WAY {
|
if member.Type == element.WAY {
|
||||||
|
|
|
@ -258,6 +258,15 @@ func (d *Deleter) Delete(delElem parser.DiffElem) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
dependers = d.diffCache.CoordsRel.Get(delElem.Node.Id)
|
||||||
|
for _, rel := range dependers {
|
||||||
|
if _, ok := d.deletedRelations[rel]; ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if err := d.deleteRelation(rel, false, false); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if !delElem.Add {
|
if !delElem.Add {
|
||||||
if err := d.diffCache.Coords.Delete(delElem.Node.Id); err != nil {
|
if err := d.diffCache.Coords.Delete(delElem.Node.Id); err != nil {
|
||||||
|
|
|
@ -175,9 +175,9 @@ func Update(oscFile string, geometryLimiter *limit.Limiter, expireor expire.Expi
|
||||||
nodeWriter.SetExpireor(expireor)
|
nodeWriter.SetExpireor(expireor)
|
||||||
nodeWriter.Start()
|
nodeWriter.Start()
|
||||||
|
|
||||||
nodeIds := make(map[int64]bool)
|
nodeIds := make(map[int64]struct{})
|
||||||
wayIds := make(map[int64]bool)
|
wayIds := make(map[int64]struct{})
|
||||||
relIds := make(map[int64]bool)
|
relIds := make(map[int64]struct{})
|
||||||
|
|
||||||
step := log.StartStep("Parsing changes, updating cache and removing elements")
|
step := log.StartStep("Parsing changes, updating cache and removing elements")
|
||||||
|
|
||||||
|
@ -251,7 +251,7 @@ func Update(oscFile string, geometryLimiter *limit.Limiter, expireor expire.Expi
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return diffError(err, "put relation %v", elem.Rel)
|
return diffError(err, "put relation %v", elem.Rel)
|
||||||
}
|
}
|
||||||
relIds[elem.Rel.Id] = true
|
relIds[elem.Rel.Id] = struct{}{}
|
||||||
}
|
}
|
||||||
} else if elem.Way != nil {
|
} else if elem.Way != nil {
|
||||||
// check if first coord is cached to avoid caching
|
// check if first coord is cached to avoid caching
|
||||||
|
@ -265,7 +265,7 @@ func Update(oscFile string, geometryLimiter *limit.Limiter, expireor expire.Expi
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return diffError(err, "put way %v", elem.Way)
|
return diffError(err, "put way %v", elem.Way)
|
||||||
}
|
}
|
||||||
wayIds[elem.Way.Id] = true
|
wayIds[elem.Way.Id] = struct{}{}
|
||||||
}
|
}
|
||||||
} else if elem.Node != nil {
|
} else if elem.Node != nil {
|
||||||
addNode := true
|
addNode := true
|
||||||
|
@ -283,7 +283,7 @@ func Update(oscFile string, geometryLimiter *limit.Limiter, expireor expire.Expi
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return diffError(err, "put coord %v", elem.Node)
|
return diffError(err, "put coord %v", elem.Node)
|
||||||
}
|
}
|
||||||
nodeIds[elem.Node.Id] = true
|
nodeIds[elem.Node.Id] = struct{}{}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -301,7 +301,7 @@ func Update(oscFile string, geometryLimiter *limit.Limiter, expireor expire.Expi
|
||||||
|
|
||||||
// mark member ways from deleted relations for re-insert
|
// mark member ways from deleted relations for re-insert
|
||||||
for id, _ := range deleter.DeletedMemberWays() {
|
for id, _ := range deleter.DeletedMemberWays() {
|
||||||
wayIds[id] = true
|
wayIds[id] = struct{}{}
|
||||||
}
|
}
|
||||||
|
|
||||||
progress.Stop()
|
progress.Stop()
|
||||||
|
@ -314,16 +314,22 @@ func Update(oscFile string, geometryLimiter *limit.Limiter, expireor expire.Expi
|
||||||
for nodeId, _ := range nodeIds {
|
for nodeId, _ := range nodeIds {
|
||||||
dependers := diffCache.Coords.Get(nodeId)
|
dependers := diffCache.Coords.Get(nodeId)
|
||||||
for _, way := range dependers {
|
for _, way := range dependers {
|
||||||
wayIds[way] = true
|
wayIds[way] = struct{}{}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// mark depending relations for (re)insert
|
// mark depending relations for (re)insert
|
||||||
|
for nodeId, _ := range nodeIds {
|
||||||
|
dependers := diffCache.CoordsRel.Get(nodeId)
|
||||||
|
for _, rel := range dependers {
|
||||||
|
relIds[rel] = struct{}{}
|
||||||
|
}
|
||||||
|
}
|
||||||
for wayId, _ := range wayIds {
|
for wayId, _ := range wayIds {
|
||||||
dependers := diffCache.Ways.Get(wayId)
|
dependers := diffCache.Ways.Get(wayId)
|
||||||
// mark depending relations for (re)insert
|
// mark depending relations for (re)insert
|
||||||
for _, rel := range dependers {
|
for _, rel := range dependers {
|
||||||
relIds[rel] = true
|
relIds[rel] = struct{}{}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -225,4 +225,11 @@
|
||||||
</relation>
|
</relation>
|
||||||
</create>
|
</create>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- test that relations are updated after modified node -->
|
||||||
|
<modify>
|
||||||
|
<node id="52101" version="1" timestamp="2011-11-11T00:11:11Z" lat="61" lon="10"/>
|
||||||
|
</modify>
|
||||||
|
|
||||||
|
|
||||||
</osmChange>
|
</osmChange>
|
||||||
|
|
|
@ -1019,6 +1019,26 @@
|
||||||
<tag k="building" v="mp"/>
|
<tag k="building" v="mp"/>
|
||||||
</relation>
|
</relation>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- test that relations are updated after modified node -->
|
||||||
|
<node id="52101" version="1" timestamp="2011-11-11T00:11:11Z" lat="62" lon="10"/>
|
||||||
|
<node id="52102" version="1" timestamp="2011-11-11T00:11:11Z" lat="62" lon="11"/>
|
||||||
|
<node id="52103" version="1" timestamp="2011-11-11T00:11:11Z" lat="64" lon="10"/>
|
||||||
|
<node id="52104" version="1" timestamp="2011-11-11T00:11:11Z" lat="64" lon="11"/>
|
||||||
|
<way id="52111" version="1" timestamp="2011-11-11T00:11:11Z">
|
||||||
|
<nd ref="52101"/>
|
||||||
|
<nd ref="52102"/>
|
||||||
|
<nd ref="52103"/>
|
||||||
|
<nd ref="52104"/>
|
||||||
|
<nd ref="52101"/>
|
||||||
|
<tag k="building" v="yes"/>
|
||||||
|
</way>
|
||||||
|
<relation id="52121" version="1" timestamp="2011-11-11T00:11:11Z">
|
||||||
|
<member type="way" ref="52111" role="outer"/>
|
||||||
|
<tag k="type" v="multipolygon"/>
|
||||||
|
</relation>
|
||||||
|
|
||||||
|
|
||||||
<!-- zig-zag line with coords internaly cached in differend deltacoords bunches -->
|
<!-- zig-zag line with coords internaly cached in differend deltacoords bunches -->
|
||||||
<node id="60001" version="1" timestamp="2011-11-11T00:11:11Z" lat="0.01" lon="20.00"/>
|
<node id="60001" version="1" timestamp="2011-11-11T00:11:11Z" lat="0.01" lon="20.00"/>
|
||||||
<node id="60002" version="1" timestamp="2011-11-11T00:11:11Z" lat="0.00" lon="20.01"/>
|
<node id="60002" version="1" timestamp="2011-11-11T00:11:11Z" lat="0.00" lon="20.01"/>
|
||||||
|
|
|
@ -297,6 +297,12 @@ func TestDuplicateIds(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestRelationUpdatedByNode(t *testing.T) {
|
||||||
|
// Relations was updated after modified node.
|
||||||
|
|
||||||
|
assertArea(t, checkElem{"osm_buildings", -52121, "yes", nil}, 13653930440.868315)
|
||||||
|
}
|
||||||
|
|
||||||
func TestGeneralizedBananaPolygonIsValid(t *testing.T) {
|
func TestGeneralizedBananaPolygonIsValid(t *testing.T) {
|
||||||
// Generalized polygons are valid.
|
// Generalized polygons are valid.
|
||||||
|
|
||||||
|
@ -605,6 +611,12 @@ func TestDuplicateIds2(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestRelationUpdatedByNode2(t *testing.T) {
|
||||||
|
// Relations was updated after modified node.
|
||||||
|
|
||||||
|
assertArea(t, checkElem{"osm_buildings", -52121, "yes", nil}, 16276875196.653734)
|
||||||
|
}
|
||||||
|
|
||||||
func TestUpdatedWay2(t *testing.T) {
|
func TestUpdatedWay2(t *testing.T) {
|
||||||
// All nodes of straightened way are updated.
|
// All nodes of straightened way are updated.
|
||||||
|
|
||||||
|
|
|
@ -9,4 +9,36 @@
|
||||||
<tag k="name" v="new name"/>
|
<tag k="name" v="new name"/>
|
||||||
</way>
|
</way>
|
||||||
</modify>
|
</modify>
|
||||||
|
|
||||||
|
<modify>
|
||||||
|
<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="halt"/> <!-- changed role -->
|
||||||
|
<!-- <member type="way" ref="100512" role="platform"/> removed -->
|
||||||
|
<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>
|
||||||
|
</modify>
|
||||||
|
|
||||||
|
<modify>
|
||||||
|
<!-- modified node updates relation -->
|
||||||
|
<node id="110101" version="1" timestamp="2015-12-31T23:59:99Z" lat="53.0" lon="8.200">
|
||||||
|
<tag k="name" v="Stop2"/>
|
||||||
|
</node>
|
||||||
|
</modify>
|
||||||
|
|
||||||
</osmChange>
|
</osmChange>
|
||||||
|
|
|
@ -128,4 +128,16 @@
|
||||||
<tag k="type" v="route_master"/>
|
<tag k="type" v="route_master"/>
|
||||||
</relation>
|
</relation>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- modified node updates relation -->
|
||||||
|
<node id="110101" version="1" timestamp="2015-12-31T23:59:99Z" lat="53.0" lon="8.200">
|
||||||
|
<tag k="name" v="Stop"/>
|
||||||
|
</node>
|
||||||
|
|
||||||
|
<relation id="110901" version="23" timestamp="2015-06-02T04:13:19Z">
|
||||||
|
<member type="node" ref="110101" role="stop"/>
|
||||||
|
<tag k="route" v="bus"/>
|
||||||
|
<tag k="type" v="route"/>
|
||||||
|
</relation>
|
||||||
|
|
||||||
</osm>
|
</osm>
|
||||||
|
|
|
@ -2,7 +2,6 @@ package test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"strconv"
|
|
||||||
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
@ -92,6 +91,7 @@ func TestRouteRelation_MemberGeomUpdated2(t *testing.T) {
|
||||||
t.Fatal(g.Length())
|
t.Fatal(g.Length())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// tag from member is updated
|
||||||
rows = ts.queryDynamic(t, "osm_route_members", "osm_id = -100902 AND member = 100503")
|
rows = ts.queryDynamic(t, "osm_route_members", "osm_id = -100902 AND member = 100503")
|
||||||
if len(rows) != 1 {
|
if len(rows) != 1 {
|
||||||
t.Fatal(rows)
|
t.Fatal(rows)
|
||||||
|
@ -99,16 +99,31 @@ func TestRouteRelation_MemberGeomUpdated2(t *testing.T) {
|
||||||
if rows[0]["name"] != "new name" {
|
if rows[0]["name"] != "new name" {
|
||||||
t.Error(rows[0])
|
t.Error(rows[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// member is removed
|
||||||
|
rows = ts.queryDynamic(t, "osm_route_members", "osm_id = -100902 AND member = 100512")
|
||||||
|
if len(rows) != 0 {
|
||||||
|
t.Fatal(rows)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRouteRelation_MemberNotUpdated(t *testing.T) {
|
// role from member is updated
|
||||||
// check that member is not updated if no node/way changed
|
rows = ts.queryDynamic(t, "osm_route_members", "osm_id = -100902 AND member = 100102")
|
||||||
rows := ts.queryDynamic(t, "osm_route_members", "osm_id = -100903 AND member = 100501")
|
|
||||||
if len(rows) != 1 {
|
if len(rows) != 1 {
|
||||||
t.Fatal(rows)
|
t.Fatal(rows)
|
||||||
}
|
}
|
||||||
if id, err := strconv.ParseInt(rows[0]["id"], 10, 32); err != nil || id > 27 {
|
if rows[0]["role"] != "halt" {
|
||||||
t.Error("member was re-inserted", rows)
|
t.Error(rows[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestRouteRelation_MemberUpdatedByNode(t *testing.T) {
|
||||||
|
// check that member is updated after node was modified
|
||||||
|
rows := ts.queryDynamic(t, "osm_route_members", "osm_id = -110901 AND member = 110101")
|
||||||
|
if len(rows) != 1 {
|
||||||
|
t.Fatal(rows)
|
||||||
|
}
|
||||||
|
if rows[0]["name"] != "Stop2" {
|
||||||
|
t.Error(rows[0])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -115,6 +115,7 @@ NextRel:
|
||||||
|
|
||||||
if inserted && rw.diffCache != nil {
|
if inserted && rw.diffCache != nil {
|
||||||
rw.diffCache.Ways.AddFromMembers(r.Id, allMembers)
|
rw.diffCache.Ways.AddFromMembers(r.Id, allMembers)
|
||||||
|
rw.diffCache.CoordsRel.AddFromMembers(r.Id, allMembers)
|
||||||
for _, member := range allMembers {
|
for _, member := range allMembers {
|
||||||
if member.Way != nil {
|
if member.Way != nil {
|
||||||
rw.diffCache.Coords.AddFromWay(member.Way)
|
rw.diffCache.Coords.AddFromWay(member.Way)
|
||||||
|
|
Loading…
Reference in New Issue