diff --git a/cache/delta.go b/cache/delta.go index 901d576..b338df9 100644 --- a/cache/delta.go +++ b/cache/delta.go @@ -93,6 +93,35 @@ func (b *coordsBunch) DeleteCoord(id int64) { } } +// PutCoord puts a single coord into the coords bunch. This function +// does support updating nodes. +func (b *coordsBunch) PutCoord(node element.Node) { + idx := sort.Search(len(b.coords), func(i int) bool { + return b.coords[i].Id >= node.Id + }) + if idx < len(b.coords) { + if b.coords[idx].Id == node.Id { + // overwrite + b.coords[idx] = node + } else { + // insert + b.coords = append(b.coords, node) + copy(b.coords[idx+1:], b.coords[idx:]) + b.coords[idx] = node + } + } else { + // append + b.coords = append(b.coords, node) + } +} + +// PutCoords puts multiple coords into the coords bunch. This bulk function +// does not support duplicate or updated nodes. +func (b *coordsBunch) PutCoords(nodes []element.Node) { + b.coords = append(b.coords, nodes...) + sort.Sort(byId(b.coords)) +} + type DeltaCoordsCache struct { cache lruList *list.List @@ -247,8 +276,14 @@ func (self *DeltaCoordsCache) PutCoords(nodes []element.Node) error { if err != nil { return err } - bunch.coords = append(bunch.coords, nodes[start:i]...) - sort.Sort(byId(bunch.coords)) + if self.linearImport { + bunch.PutCoords(nodes[start:i]) + } else { + for _, node := range nodes[start:i] { + // single inserts to handle updated coords + bunch.PutCoord(node) + } + } bunch.needsWrite = true bunch.Unlock() } @@ -260,8 +295,16 @@ func (self *DeltaCoordsCache) PutCoords(nodes []element.Node) error { if err != nil { return err } - bunch.coords = append(bunch.coords, nodes[start:]...) - sort.Sort(byId(bunch.coords)) + + if self.linearImport { + bunch.PutCoords(nodes[start:]) + } else { + for _, node := range nodes[start:] { + // single inserts to handle updated coords + bunch.PutCoord(node) + } + } + bunch.needsWrite = true bunch.Unlock() return nil diff --git a/cache/delta_test.go b/cache/delta_test.go index a7e524a..f635292 100644 --- a/cache/delta_test.go +++ b/cache/delta_test.go @@ -112,20 +112,76 @@ func checkReadWriteDeltaCoords(t *testing.T, withLinearImport bool) { } } - _, err = cache.GetCoord(999999) - if err != NotFound { - t.Error("missing node returned not NotFound") - } + // test overwrite + insertAndCheck(t, cache, 100, 50, 50) // test delete - cache.PutCoords([]element.Node{mknode(999999)}) - _, err = cache.GetCoord(999999) - if err == NotFound { - t.Error("missing coord") + if err != NotFound { + t.Error("found missing node") } - err = cache.DeleteCoord(999999) + insertAndCheck(t, cache, 999999, 10, 10) + deleteAndCheck(t, cache, 999999) +} + +func insertAndCheck(t *testing.T, cache *DeltaCoordsCache, id int64, lon, lat float64) { + newNode := mknode(id) + newNode.Long = lon + newNode.Lat = lat + + err := cache.PutCoords([]element.Node{newNode}) if err != nil { - t.Fatal(err) + t.Errorf("error during PutCoords for %v: %s", newNode, err) + } + + result, err := cache.GetCoord(id) + if err != nil { + t.Errorf("got error after getting inserted node %d: %s", id, err) + } + if result == nil || result.Long != lon || result.Lat != lat { + t.Errorf("invalid coords %f, %f != %v", lon, lat, result) } } + +func deleteAndCheck(t *testing.T, cache *DeltaCoordsCache, id int64) { + err := cache.DeleteCoord(id) + if err != nil { + t.Errorf("error during DeleteCoord for %d: %s", id, err) + } + + result, err := cache.GetCoord(id) + if err != NotFound { + t.Error("found deleted coord", result) + } +} + +func TestSingleUpdate(t *testing.T) { + cache_dir, _ := ioutil.TempDir("", "imposm3_test") + defer os.RemoveAll(cache_dir) + + cache, err := newDeltaCoordsCache(cache_dir) + if err != nil { + t.Fatal() + } + + // insert and update in empty batch + insertAndCheck(t, cache, 123, 10, 10) + insertAndCheck(t, cache, 123, 10, 11) + + // insert and update in same batch + insertAndCheck(t, cache, 1, 1, 1) + insertAndCheck(t, cache, 2, 2, 2) + insertAndCheck(t, cache, 3, 3, 3) + insertAndCheck(t, cache, 4, 4, 4) + insertAndCheck(t, cache, 3, 10, 11) + insertAndCheck(t, cache, 2, 10, 11) + insertAndCheck(t, cache, 1, 10, 11) + insertAndCheck(t, cache, 4, 10, 11) + // repeat after flushing + cache.Flush() + insertAndCheck(t, cache, 1, 1, 1) + insertAndCheck(t, cache, 2, 2, 2) + insertAndCheck(t, cache, 3, 3, 3) + insertAndCheck(t, cache, 4, 4, 4) + +} diff --git a/test/imposm_system_test.py b/test/imposm_system_test.py index eda7506..a71b694 100644 --- a/test/imposm_system_test.py +++ b/test/imposm_system_test.py @@ -446,6 +446,11 @@ def test_relation_with_gap(): park = query_row(db_conf, 'osm_landusages', -7301) assert park['geometry'].is_valid, park +def test_updated_nodes1(): + """Zig-Zag line is inserted.""" + road = query_row(db_conf, 'osm_roads', 60000) + assert_almost_equal(road['geometry'].length, 14035.61150207768) + ####################################################################### def test_update(): """Diff import applies""" @@ -588,6 +593,12 @@ def test_duplicate_ids2(): assert query_row(db_conf, 'osm_buildings', -51011)['type'] == 'mp' assert query_row(db_conf, 'osm_buildings', 51011) == None +def test_updated_way2(): + """All nodes of straightened way are updated.""" + road = query_row(db_conf, 'osm_roads', 60000) + # new length 0.1 degree + assert_almost_equal(road['geometry'].length, 20037508.342789244/180.0/10.0) + ####################################################################### def test_deploy_and_revert_deploy(): """Revert deploy succeeds""" diff --git a/test/test.osc b/test/test.osc index 117c623..79e2a4f 100644 --- a/test/test.osc +++ b/test/test.osc @@ -165,6 +165,15 @@ - - + + + + + + + + + + + diff --git a/test/test.osm b/test/test.osm index 58eac63..db7de05 100644 --- a/test/test.osm +++ b/test/test.osm @@ -898,4 +898,24 @@ + + + + + + + + + + + + + + + + + + + +