2022-10-23 21:35:04 +00:00
|
|
|
# Salem Oregon Address Import
|
|
|
|
|
|
|
|
See [https://wiki.openstreetmap.org/wiki/Salem_Oregon_Address_Import](https://wiki.openstreetmap.org/wiki/Salem_Oregon_Address_Import)
|
|
|
|
|
|
|
|
## Data Source
|
|
|
|
|
|
|
|
- Salem GIS department's FTP server
|
|
|
|
- Also https://data.cityofsalem.net/datasets/salem::primary-address/explore
|
|
|
|
- No copyright, public domain
|
|
|
|
|
2023-10-26 04:34:41 +00:00
|
|
|
<img src="Screenshot_2023-10-25_213128.png"/>
|
|
|
|
|
2022-10-23 21:35:04 +00:00
|
|
|
## Field Mapping
|
|
|
|
|
2023-10-26 05:11:21 +00:00
|
|
|
- ADD_NUM -> addr:housenumber (int)
|
|
|
|
- formatstreet(FULL_ST_NAME) -> addr:street (text)
|
|
|
|
- SUB_VAL -> addr:unit (text)
|
|
|
|
- title(CITY) -> addr:city (text)
|
|
|
|
- ZIP -> addr:postcode (text)
|
|
|
|
- addr:state 'OR' (manually added)
|
2022-10-23 21:35:04 +00:00
|
|
|
|
|
|
|
## Processing
|
|
|
|
|
2023-10-26 05:11:21 +00:00
|
|
|
- Get `Primary_Addresses.geojson` from the City GIS server linked above.
|
|
|
|
- Open the file in QGIS and open the layer attribute table
|
|
|
|
- Use the field calculator and the below script to add new virtual fields per the mapping above.
|
|
|
|
- Export the layer to geojson with "EPSG:4326 - WGS 84" projection. Deselect all fields besides those above.
|
2022-10-23 21:35:04 +00:00
|
|
|
- Save as `processed.geojson`
|
|
|
|
|
|
|
|
## Import steps
|
|
|
|
|
|
|
|
- Load processed.geojson into JOSM
|
|
|
|
- Zoom in to the area you wish to work on and Download OSM data to a New Layer
|
|
|
|
- In the geojson layer, select one neighborhood or city block worth of addresses
|
|
|
|
- Click Edit > Merge Selection
|
2023-10-26 05:29:35 +00:00
|
|
|
- Delete the selected items you just merged (so you don't try merging them again later)
|
2022-10-23 21:35:04 +00:00
|
|
|
- Switch to Data Layer 1 and run the JOSM Validator
|
2023-10-26 05:23:51 +00:00
|
|
|
- An easy way to only validate changes is to press the Upload button, but cancel before actually uploading.
|
|
|
|
- We don't need to worry about validation errors that don't involve our changes.
|
2022-10-23 21:35:04 +00:00
|
|
|
- Fix all duplicate housenumber warnings and nearby street not found warnings
|
2023-10-26 05:23:51 +00:00
|
|
|
- An easy way to auto-fix all duplicate housenumbers is to select all duplicates, Search within the selection for `new`, and delete.
|
|
|
|
- Pay special attention to errors like "East Street Northeast" -> "E Street Northeast" which can be mass-corrected.
|
2023-10-26 05:29:35 +00:00
|
|
|
- Search for all `new "addr:housenumber" = "0"` elements and delete them.
|
2022-10-23 21:35:04 +00:00
|
|
|
- Click Upload, verify there are no further warnings or errors in the changeset
|
|
|
|
- Make sure there are no erroneous Relations or other unwanted objects about to be uploaded.
|
|
|
|
- Upload with this changeset comment:
|
|
|
|
|
|
|
|
```
|
|
|
|
comment=Addresses near Salem Oregon #salemimport
|
|
|
|
import=yes
|
|
|
|
website=https://wiki.openstreetmap.org/wiki/Salem_Oregon_Address_Import
|
|
|
|
source=City of Salem GIS
|
|
|
|
source:url=https://data.cityofsalem.net/datasets/salem::primary-address/explore
|
|
|
|
```
|
|
|
|
|
|
|
|
- Review imported data in Achavi or Osmcha to ensure it looks proper.
|
|
|
|
|
|
|
|
## QGIS Processing script
|
|
|
|
|
|
|
|
```
|
2023-10-26 05:11:21 +00:00
|
|
|
import qgis.core
|
|
|
|
import qgis.gui
|
2022-10-23 21:35:04 +00:00
|
|
|
import re
|
|
|
|
|
2023-10-26 05:11:21 +00:00
|
|
|
#
|
|
|
|
# This will keep street names like SR 574A as SR 574A however
|
|
|
|
# will lowercase other number-digit suffixes with <2 or >4 numbers
|
|
|
|
# or >1 suffix-letters, like 12th Street or 243rd Ave.
|
|
|
|
#
|
|
|
|
|
2022-10-23 21:35:04 +00:00
|
|
|
@qgsfunction(args='auto', group='Custom', referenced_columns=[])
|
2023-10-26 05:11:21 +00:00
|
|
|
def getstreetfromaddress(value1, feature, parent):
|
2022-10-23 21:35:04 +00:00
|
|
|
parts = value1.split()
|
2023-10-26 05:11:21 +00:00
|
|
|
parts.pop(0) # Ignore the first bit (i.e. "123" in "123 N MAIN ST")
|
2022-10-23 21:35:04 +00:00
|
|
|
parts = map(formatstreetname, parts)
|
|
|
|
return " ".join(parts)
|
|
|
|
|
|
|
|
@qgsfunction(args='auto', group='Custom', referenced_columns=[])
|
2023-10-26 05:11:21 +00:00
|
|
|
def formatstreet(value1, feature, parent):
|
2022-10-23 21:35:04 +00:00
|
|
|
parts = value1.split()
|
|
|
|
parts = map(formatstreetname, parts)
|
|
|
|
return " ".join(parts)
|
|
|
|
|
2023-10-26 05:11:21 +00:00
|
|
|
# Internal function
|
2022-10-23 21:35:04 +00:00
|
|
|
def formatstreetname(name):
|
2023-10-26 05:11:21 +00:00
|
|
|
# Acryonyms
|
|
|
|
if name == "CR":
|
|
|
|
return "County Road"
|
|
|
|
if name == "SR":
|
|
|
|
return "SR" # State Route
|
|
|
|
if name == "NFS":
|
|
|
|
return "NFS" # National Forest Service?
|
2022-10-23 21:35:04 +00:00
|
|
|
if name == "US":
|
|
|
|
return "US"
|
|
|
|
# Directions
|
|
|
|
if name == "N":
|
|
|
|
return "North"
|
|
|
|
if name == "NE":
|
|
|
|
return "Northeast"
|
|
|
|
if name == "E":
|
|
|
|
return "East"
|
|
|
|
if name == "SE":
|
|
|
|
return "Southeast"
|
|
|
|
if name == "S":
|
|
|
|
return "South"
|
|
|
|
if name == "SW":
|
|
|
|
return "Southwest"
|
|
|
|
if name == "W":
|
|
|
|
return "West"
|
|
|
|
if name == "NW":
|
|
|
|
return "Northwest"
|
|
|
|
# Suffixes
|
|
|
|
if name == "AVE":
|
|
|
|
return "Avenue"
|
|
|
|
if name == "BLVD":
|
|
|
|
return "Boulevard"
|
|
|
|
if name == "BND":
|
|
|
|
return "Bend"
|
|
|
|
if name == "CIR":
|
|
|
|
return "Circle"
|
2023-10-26 05:11:21 +00:00
|
|
|
if name == "CR":
|
|
|
|
return "Circle"
|
2022-10-23 21:35:04 +00:00
|
|
|
if name == "CT":
|
|
|
|
return "Court"
|
|
|
|
if name == "DR":
|
|
|
|
return "Drive"
|
|
|
|
if name == "FLDS":
|
|
|
|
return "Fields"
|
|
|
|
if name == "GRV":
|
|
|
|
return "Grove"
|
2023-10-26 05:11:21 +00:00
|
|
|
if name == "HL":
|
|
|
|
return "Hill"
|
2022-10-23 21:35:04 +00:00
|
|
|
if name == "HOLW":
|
|
|
|
return "Hollow"
|
|
|
|
if name == "HWY":
|
|
|
|
return "Highway"
|
|
|
|
if name == "LN":
|
|
|
|
return "Lane"
|
|
|
|
if name == "LOOP":
|
|
|
|
return "Loop"
|
2023-10-26 05:11:21 +00:00
|
|
|
if name == "LP":
|
|
|
|
return "Loop"
|
2022-10-23 21:35:04 +00:00
|
|
|
if name == "PATH":
|
|
|
|
return "Path"
|
|
|
|
if name == "PL":
|
|
|
|
return "Place"
|
|
|
|
if name == "RD":
|
|
|
|
return "Road"
|
|
|
|
if name == "RUN":
|
|
|
|
return "Run"
|
2023-10-26 05:11:21 +00:00
|
|
|
if name == "SQ":
|
|
|
|
return "Square"
|
2022-10-23 21:35:04 +00:00
|
|
|
if name == "ST":
|
|
|
|
return "Street"
|
|
|
|
if name == "TER":
|
|
|
|
return "Terrace"
|
|
|
|
if name == "TRL":
|
|
|
|
return "Trail"
|
|
|
|
if name == "VW":
|
|
|
|
return "View"
|
|
|
|
if name == "WAY":
|
|
|
|
return "Way"
|
2023-10-26 05:11:21 +00:00
|
|
|
if name == "WY":
|
|
|
|
return "Way"
|
2022-10-23 21:35:04 +00:00
|
|
|
if name == "XING":
|
|
|
|
return "Crossing"
|
2023-10-26 05:11:21 +00:00
|
|
|
if re.match('^[0-9]{2,4}[A-Za-z]$', name) != None:
|
|
|
|
return name
|
|
|
|
|
2022-10-23 21:35:04 +00:00
|
|
|
return name.capitalize()
|
|
|
|
```
|