Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Change GeoJSON geometry when tag suggests area feature #227

Merged
merged 2 commits into from
May 19, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 38 additions & 2 deletions app/screens/Features/AddFeatureDetail.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,13 @@ import { getParentPreset } from '../../utils/get-parent-preset'
import { colors } from '../../style/variables'
import { KeyboardAwareScrollView } from 'react-native-keyboard-aware-scroll-view'
import { modes } from '../../utils/map-modes'
import { osmTagSuggestingArea } from '../../utils/osm-tag-suggesting-area'

const FieldsList = styled.FlatList`
`
const ScrollView = styled.ScrollView`
background-color: white
`

class EditFeatureDetail extends React.Component {
static navigationOptions = ({ navigation }) => {
return {
Expand Down Expand Up @@ -151,7 +151,7 @@ class EditFeatureDetail extends React.Component {
feature.properties = this.getFeatureProperties()

// call addFeature action with feature
this.props.addFeature(feature, comment)
this.props.addFeature(this.setGeometryFromTags(feature), comment)

// call action to attempt to upload the edit
this.props.uploadEdits([feature.id])
Expand All @@ -170,6 +170,42 @@ class EditFeatureDetail extends React.Component {
return _omitBy(props, prop => !prop)
}

setGeometryFromTags (feature) {
const { geometry } = feature

// Do not update feature of type 'Point'
if (geometry.type === 'Point') return feature

// Check for area tags
const tags = _omit(feature.properties, ['id', 'version', 'ndrefs'])
const areaTags = osmTagSuggestingArea(tags)

// Area tags were found and feature is LineString, change to Polygon
if (areaTags && geometry.type === 'LineString') {
return {
...feature,
geometry: {
type: 'Polygon',
coordinates: [geometry.coordinates]
}
}
}

// Area tags were NOT found and feature is Polygon, change to LineString
if (!areaTags && feature.type === 'Polygon') {
return {
...feature,
geometry: {
type: 'LineString',
coordinates: geometry.coordinates[0]
}
}
}

// Feature type should consistent with tags, return unchanged
return feature
}

willBlur () {
this.resetState()
}
Expand Down
98 changes: 98 additions & 0 deletions app/utils/osm-tag-suggesting-area.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import { presets as allPresets } from '../presets/presets.json'
import presetsObjectToArray from './object-to-array'

// This module is based on the approach of iD editor:
// https://github.com/openstreetmap/iD/blob/7b09b6c0dcd193af04926727b820f346a1d86ca8/modules/osm/tags.js#L17

const presetsArray = presetsObjectToArray(allPresets)

/**
* Create a reference object with tags suggesting an area
*/
function getAreaTags () {
// The ignore list is for keys that imply lines. (We always add `area=yes` for exceptions)
const ignore = [
'barrier',
'highway',
'footway',
'railway',
'junction',
'type'
]
let areaKeys = {}

// ignore name-suggestion-index and deprecated presets
const presets = presetsArray.filter((p) => !p.suggestion && !p.replacement)

// keeplist
presets.forEach((p) => {
let key
for (key in p.tags) break // pick the first tag
if (!key) return
if (ignore.indexOf(key) !== -1) return

if (p.geometry.indexOf('area') !== -1) {
// probably an area..
areaKeys[key] = areaKeys[key] || {}
}
})

// discardlist
presets.forEach((p) => {
let key
for (key in p.addTags) {
// examine all addTags to get a better sense of what can be tagged on lines - #6800
const value = p.addTags[key]
if (
key in areaKeys && // probably an area...
p.geometry.indexOf('line') !== -1 && // but sometimes a line
value !== '*'
) {
areaKeys[key][value] = true
}
}
})

return areaKeys
}

// Init object on load
const osmAreaKeys = getAreaTags()

/**
* Identifies if a set of tags suggests an area feature and returns associated
* tags.
*/
export function osmTagSuggestingArea (tags) {
if (tags.area === 'yes') return { area: 'yes' }
if (tags.area === 'no') return null

// `highway` and `railway` are typically linear features, but there
// are a few exceptions that should be treated as areas, even in the
// absence of a proper `area=yes` or `areaKeys` tag.. see #4194
var lineKeys = {
highway: {
rest_area: true,
services: true
},
railway: {
roundhouse: true,
station: true,
traverser: true,
turntable: true,
wash: true
}
}
var returnTags = {}
for (var key in tags) {
if (key in osmAreaKeys && !(tags[key] in osmAreaKeys[key])) {
returnTags[key] = tags[key]
return returnTags
}
if (key in lineKeys && tags[key] in lineKeys[key]) {
returnTags[key] = tags[key]
return returnTags
}
}
return null
}