Skip to content

Commit

Permalink
Merge pull request #1 from timonmasberg/relation-outer-members-centroid
Browse files Browse the repository at this point in the history
Relation outer members centroid
  • Loading branch information
timonmasberg authored Sep 9, 2021
2 parents aae551d + 7f9f617 commit 136e779
Show file tree
Hide file tree
Showing 5 changed files with 464 additions and 77 deletions.
14 changes: 7 additions & 7 deletions centroid_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ func TestComputeCentroidWithEntranceNode(t *testing.T) {
map[string]string{"lat": "1", "lon": "2", "entrance": "1"},
}

var centroid, bounds = computeCentroidAndBounds(latlons)
var centroid, bounds = ComputeCentroidAndBounds(latlons)
assert.Equal(t, "1", centroid["lat"])
assert.Equal(t, "2", centroid["lon"])
assert.Equal(t, +1.0, bounds.North())
Expand All @@ -29,7 +29,7 @@ func TestComputeCentroidWithMainEntranceNode(t *testing.T) {
map[string]string{"lat": "-1", "lon": "-2", "entrance": "1", "wheelchair": "2"},
}

var centroid, bounds = computeCentroidAndBounds(latlons)
var centroid, bounds = ComputeCentroidAndBounds(latlons)
assert.Equal(t, "1", centroid["lat"])
assert.Equal(t, "2", centroid["lon"])
assert.Equal(t, +1.0, bounds.North())
Expand All @@ -45,7 +45,7 @@ func TestComputeCentroidWithAccessibleEntranceNode(t *testing.T) {
map[string]string{"lat": "-1", "lon": "-2", "entrance": "1", "wheelchair": "2"},
}

var centroid, bounds = computeCentroidAndBounds(latlons)
var centroid, bounds = ComputeCentroidAndBounds(latlons)
assert.Equal(t, "-1", centroid["lat"])
assert.Equal(t, "-2", centroid["lon"])
assert.Equal(t, +0.0, bounds.North())
Expand All @@ -60,7 +60,7 @@ func TestComputeCentroidWithRegularEntranceNode(t *testing.T) {
map[string]string{"lat": "0", "lon": "0", "entrance": "1"},
}

var centroid, bounds = computeCentroidAndBounds(latlons)
var centroid, bounds = ComputeCentroidAndBounds(latlons)
assert.Equal(t, "0", centroid["lat"])
assert.Equal(t, "0", centroid["lon"])
assert.Equal(t, +0.0, bounds.North())
Expand All @@ -79,7 +79,7 @@ func TestComputeCentroidForClosedPolygon(t *testing.T) {
map[string]string{"lat": "1", "lon": "1"},
}

var centroid, bounds = computeCentroidAndBounds(latlons)
var centroid, bounds = ComputeCentroidAndBounds(latlons)
assert.Equal(t, "0.0000000", centroid["lat"])
assert.Equal(t, "0.0000000", centroid["lon"])
assert.Equal(t, +1.0, bounds.North())
Expand All @@ -100,7 +100,7 @@ func TestComputeCentroidForHillboroPublicLibrary(t *testing.T) {
map[string]string{"lat": "45.5424694", "lon": "-122.9356798"},
}

var centroid, bounds = computeCentroidAndBounds(latlons)
var centroid, bounds = ComputeCentroidAndBounds(latlons)
assert.Equal(t, "45.5428760", centroid["lat"])
assert.Equal(t, "-122.9359955", centroid["lon"])
assert.Equal(t, +45.5433259, bounds.North())
Expand All @@ -117,7 +117,7 @@ func TestComputeCentroidForOpenLineString(t *testing.T) {
map[string]string{"lat": "-1", "lon": "-1"},
}

var centroid, bounds = computeCentroidAndBounds(latlons)
var centroid, bounds = ComputeCentroidAndBounds(latlons)
assert.Equal(t, "0.0000000", centroid["lat"])
assert.Equal(t, "0.0000000", centroid["lon"])
assert.Equal(t, +1.0, bounds.North())
Expand Down
103 changes: 33 additions & 70 deletions pbf2json.go
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,7 @@ func print(d *osmpbf.Decoder, masks *BitmaskMap, db *leveldb.DB, config settings
}

// compute centroid
centroid, bounds := computeCentroidAndBounds(latlons)
centroid, bounds := ComputeCentroidAndBounds(latlons)

// trim tags
v.Tags = trimTags(v.Tags)
Expand Down Expand Up @@ -318,55 +318,15 @@ func print(d *osmpbf.Decoder, masks *BitmaskMap, db *leveldb.DB, config settings
continue
}

// best centroid and bounds to use
var largestArea = 0.0
var centroid map[string]string
var bounds *geo.Bound
// consult https://wiki.openstreetmap.org/wiki/DE:Relation:multipolygon on how osm handles outer members
centroid, bounds := ComputeRelationCentroidAndBounds(memberWayLatLons)

// iterate over each way, selecting the largest way to use
// for the centroid and bbox
for _, latlons := range memberWayLatLons {

// compute centroid
wayCentroid, wayBounds := computeCentroidAndBounds(latlons)

// if for any reason we failed to find a valid bounds
if nil == wayBounds {
log.Println("[warn] failed to calculate bounds for relation member way")
continue
}

area := math.Max(wayBounds.GeoWidth(), 0.000001) * math.Max(wayBounds.GeoHeight(), 0.000001)

// find the way with the largest area
if area > largestArea {
largestArea = area
centroid = wayCentroid
bounds = wayBounds
}
}

// if for any reason we failed to find a valid bounds
if nil == bounds {
log.Println("[warn] denormalize failed for relation:", v.ID, "no valid bounds")
if centroid == nil || bounds == nil {
// the relation is probably not a whole part of the osm dump
log.Printf("[warn] could not find centroid and bounds of %d", v.ID)
continue
}

// use 'admin_centre' node centroid where available
// note: only applies to 'boundary=administrative' relations
// see: https://github.com/pelias/pbf2json/pull/98
if v.Tags["boundary"] == "administrative" {
for _, member := range v.Members {
if member.Type == 0 && member.Role == "admin_centre" {
if latlons, err := cacheLookupNodeByID(db, member.ID); err == nil {
latlons["type"] = "admin_centre"
centroid = latlons
break
}
}
}
}

// trim tags
v.Tags = trimTags(v.Tags)

Expand All @@ -384,22 +344,19 @@ func print(d *osmpbf.Decoder, masks *BitmaskMap, db *leveldb.DB, config settings
}

// lookup all latlons for all ways in relation
func findMemberWayLatLons(db *leveldb.DB, v *osmpbf.Relation) [][]map[string]string {
var memberWayLatLons [][]map[string]string
func findMemberWayLatLons(db *leveldb.DB, v *osmpbf.Relation) map[osmpbf.Member][]map[string]string {
var memberWayLatLons = make(map[osmpbf.Member][]map[string]string)

for _, mem := range v.Members {
if mem.Type == 1 {

// lookup from leveldb
latlons, err := cacheLookupWayNodes(db, mem.ID)

// skip way if it fails to denormalize
if err != nil {
break
}
// lookup from leveldb
latlons, err := cacheLookupWayNodes(db, mem.ID)

memberWayLatLons = append(memberWayLatLons, latlons)
// skip way if it fails to denormalize
if err != nil {
break
}

memberWayLatLons[mem] = latlons
}

return memberWayLatLons
Expand Down Expand Up @@ -685,10 +642,7 @@ func selectEntrance(entrances []map[string]string) map[string]string {
return centroid
}

// compute the centroid of a way and its bbox
func computeCentroidAndBounds(latlons []map[string]string) (map[string]string, *geo.Bound) {

// check to see if there is a tagged entrance we can use.
func getEntrance(latlons []map[string]string) (bool, map[string]string, *geo.Bound) {
var entrances []map[string]string
for _, latlon := range latlons {
if _, ok := latlon["entrance"]; ok {
Expand All @@ -706,15 +660,26 @@ func computeCentroidAndBounds(latlons []map[string]string) (map[string]string, *

// use the mapped entrance location where available
if len(entrances) > 0 {
return selectEntrance(entrances), points.Bound()
return true, selectEntrance(entrances), points.Bound()
}

return false, nil, nil
}

// ComputeCentroidAndBounds compute the centroid of a way and its bbox for polygons and lines
func ComputeCentroidAndBounds(latlons []map[string]string) (map[string]string, *geo.Bound) {
hasEntrance, entranceLatLon, entranceBounds := getEntrance(latlons)

if hasEntrance {
return entranceLatLon, entranceBounds
}

// convert lat/lon map to geo.PointSet
points := LatLngMapToPointSet(latlons)

// determine if the way is a closed centroid or a linestring
// by comparing first and last coordinates.
isClosed := false
if points.Length() > 2 {
isClosed = points.First().Equals(points.Last())
}
isClosed := IsPointSetClosed(points)

// compute the centroid using one of two different algorithms
var compute *geo.Point
Expand All @@ -725,9 +690,7 @@ func computeCentroidAndBounds(latlons []map[string]string) (map[string]string, *
}

// return point as lat/lon map
var centroid = make(map[string]string)
centroid["lat"] = strconv.FormatFloat(compute.Lat(), 'f', 7, 64)
centroid["lon"] = strconv.FormatFloat(compute.Lng(), 'f', 7, 64)
centroid := PointToLatLon(compute)

return centroid, points.Bound()
}
Loading

0 comments on commit 136e779

Please sign in to comment.