pelias-openstreetmap 8.3.0 → 8.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/_test.yml +3 -3
- package/.github/workflows/push.yml +11 -9
- package/README.md +20 -0
- package/config/features.js +54 -46
- package/package.json +4 -4
- package/schema.js +8 -1
- package/stream/document_constructor.js +9 -2
- package/stream/importPipeline.js +2 -0
- package/stream/pbf.js +6 -6
- package/stream/venueFilter.js +30 -0
- package/util/layerClassifier.js +43 -0
- package/util/resolveFeatures.js +25 -0
|
@@ -6,12 +6,12 @@ jobs:
|
|
|
6
6
|
strategy:
|
|
7
7
|
matrix:
|
|
8
8
|
os:
|
|
9
|
-
- ubuntu-
|
|
9
|
+
- ubuntu-24.04
|
|
10
10
|
node-version: [ 20.x, 22.x, 24.x ]
|
|
11
11
|
steps:
|
|
12
|
-
- uses: actions/checkout@
|
|
12
|
+
- uses: actions/checkout@v6
|
|
13
13
|
- name: 'Install node.js ${{ matrix.node-version }}'
|
|
14
|
-
uses: actions/setup-node@
|
|
14
|
+
uses: actions/setup-node@v6
|
|
15
15
|
with:
|
|
16
16
|
node-version: '${{ matrix.node-version }}'
|
|
17
17
|
- name: Run unit tests
|
|
@@ -6,19 +6,21 @@ jobs:
|
|
|
6
6
|
npm-publish:
|
|
7
7
|
needs: unit-tests
|
|
8
8
|
if: github.ref == 'refs/heads/master' && needs.unit-tests.result == 'success'
|
|
9
|
-
runs-on: ubuntu-
|
|
9
|
+
runs-on: ubuntu-24.04
|
|
10
|
+
permissions:
|
|
11
|
+
id-token: write
|
|
12
|
+
contents: write
|
|
10
13
|
steps:
|
|
11
|
-
- uses: actions/checkout@
|
|
14
|
+
- uses: actions/checkout@v6
|
|
12
15
|
- name: Install Node.js
|
|
13
|
-
uses: actions/setup-node@
|
|
16
|
+
uses: actions/setup-node@v6
|
|
14
17
|
with:
|
|
15
|
-
node-version:
|
|
18
|
+
node-version: 22.x
|
|
16
19
|
- name: Run semantic-release
|
|
17
20
|
env:
|
|
18
|
-
|
|
19
|
-
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
|
|
21
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
20
22
|
run: >
|
|
21
|
-
if [[
|
|
23
|
+
if [[ "${{ github.repository_owner }}" == "pelias" ]]; then
|
|
22
24
|
curl "https://raw.githubusercontent.com/pelias/ci-tools/master/semantic-release.sh" | bash -
|
|
23
25
|
fi
|
|
24
26
|
build-docker-images:
|
|
@@ -26,9 +28,9 @@ jobs:
|
|
|
26
28
|
# note: github actions won't run a job if you don't call one of the status check functions, so `always()` is called since it evalutes to `true`
|
|
27
29
|
if: ${{ always() && needs.unit-tests.result == 'success' && (needs.npm-publish.result == 'success' || needs.npm-publish.result == 'skipped') }}
|
|
28
30
|
needs: [unit-tests, npm-publish]
|
|
29
|
-
runs-on: ubuntu-
|
|
31
|
+
runs-on: ubuntu-24.04
|
|
30
32
|
steps:
|
|
31
|
-
- uses: actions/checkout@
|
|
33
|
+
- uses: actions/checkout@v6
|
|
32
34
|
- name: Build Docker images
|
|
33
35
|
env:
|
|
34
36
|
DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
|
package/README.md
CHANGED
|
@@ -94,6 +94,24 @@ The OSM importer will look for a file with a name matching this value in the con
|
|
|
94
94
|
|
|
95
95
|
If downloading from a remote URL, the filename must match the value in `sourceURL`.
|
|
96
96
|
|
|
97
|
+
#### `imports.openstreetmap.layers`
|
|
98
|
+
|
|
99
|
+
This is an object you can use to define your own layers based on OSM tags. For example, if you wanted a layer `coffee` for just coffee shops you could set:
|
|
100
|
+
|
|
101
|
+
```
|
|
102
|
+
{
|
|
103
|
+
"imports": {
|
|
104
|
+
"openstreetmap": {
|
|
105
|
+
"layers": {
|
|
106
|
+
"coffee": {
|
|
107
|
+
"tags": [ "amenity~cafe+name" ]
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
```
|
|
114
|
+
|
|
97
115
|
#### `imports.openstreetmap.leveldbpath`
|
|
98
116
|
|
|
99
117
|
This is the directory where temporary files will be stored in order to
|
|
@@ -106,6 +124,8 @@ Defaults to `tmp`.
|
|
|
106
124
|
|
|
107
125
|
By default, the OSM importer imports both venue records and addresses. If set to false, only address records will be imported.
|
|
108
126
|
|
|
127
|
+
You can also disable venue import by setting `imports.openstreetmap.layers.venue` to an empty value.
|
|
128
|
+
|
|
109
129
|
#### `imports.openstreetmap.removeDisusedVenues`
|
|
110
130
|
|
|
111
131
|
If set to boolean `true`, `venue`s with popularity below `0` (as determined by OSM tags) will be
|
package/config/features.js
CHANGED
|
@@ -2,52 +2,60 @@
|
|
|
2
2
|
/**
|
|
3
3
|
default list of tags to extract from the pbf file when running
|
|
4
4
|
imports. @see: https://github.com/pelias/pbf2json for more info.
|
|
5
|
-
**/
|
|
6
5
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
'addr:housenumber+addr:place' // @ref https://github.com/pelias/pelias/issues/787#issuecomment-477137803
|
|
11
|
-
];
|
|
6
|
+
addressTags: extracted so address_extractor can assign the address layer downstream.
|
|
7
|
+
these are not a layer themselves — address classification is structural,
|
|
8
|
+
not tag-based.
|
|
12
9
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
'building+name',
|
|
17
|
-
'shop+name',
|
|
18
|
-
'office+name',
|
|
19
|
-
'public_transport+name',
|
|
20
|
-
'cuisine+name',
|
|
21
|
-
'railway~station+name',
|
|
22
|
-
'railway~tram_stop+name',
|
|
23
|
-
'railway~halt+name',
|
|
24
|
-
'railway~subway_entrance+name',
|
|
25
|
-
'railway~train_station_entrance+name',
|
|
26
|
-
'highway~pedestrian+area~yes+name',
|
|
27
|
-
'place~square+name',
|
|
28
|
-
'sport+name',
|
|
29
|
-
'natural+name',
|
|
30
|
-
'tourism+name',
|
|
31
|
-
'leisure+name',
|
|
32
|
-
'historic+name',
|
|
33
|
-
'man_made+name',
|
|
34
|
-
'landuse+name',
|
|
35
|
-
'waterway+name',
|
|
36
|
-
'aerialway+name',
|
|
37
|
-
'craft+name',
|
|
38
|
-
'military+name',
|
|
39
|
-
'aeroway~terminal+name',
|
|
40
|
-
'aeroway~aerodrome+name',
|
|
41
|
-
'aeroway~helipad+name',
|
|
42
|
-
'aeroway~airstrip+name',
|
|
43
|
-
'aeroway~heliport+name',
|
|
44
|
-
'aeroway~areodrome+name',
|
|
45
|
-
'aeroway~spaceport+name',
|
|
46
|
-
'aeroway~landing_strip+name',
|
|
47
|
-
'aeroway~airfield+name',
|
|
48
|
-
'aeroway~airport+name',
|
|
49
|
-
'brand+name',
|
|
50
|
-
'healthcare+name'
|
|
51
|
-
];
|
|
10
|
+
layers: keyed by output layer name. order determines classifier priority (first match wins).
|
|
11
|
+
add custom layers here (e.g. river, protected_area) to extend the importer.
|
|
12
|
+
**/
|
|
52
13
|
|
|
53
|
-
module.exports = {
|
|
14
|
+
module.exports = {
|
|
15
|
+
addressTags: [
|
|
16
|
+
'addr:housenumber+addr:street',
|
|
17
|
+
'addr:housenumber+addr:place' // @ref https://github.com/pelias/pelias/issues/787#issuecomment-477137803
|
|
18
|
+
],
|
|
19
|
+
layers: {
|
|
20
|
+
venue: {
|
|
21
|
+
tags: [
|
|
22
|
+
'amenity+name',
|
|
23
|
+
'building+name',
|
|
24
|
+
'shop+name',
|
|
25
|
+
'office+name',
|
|
26
|
+
'public_transport+name',
|
|
27
|
+
'cuisine+name',
|
|
28
|
+
'railway~station+name',
|
|
29
|
+
'railway~tram_stop+name',
|
|
30
|
+
'railway~halt+name',
|
|
31
|
+
'railway~subway_entrance+name',
|
|
32
|
+
'railway~train_station_entrance+name',
|
|
33
|
+
'highway~pedestrian+area~yes+name',
|
|
34
|
+
'place~square+name',
|
|
35
|
+
'sport+name',
|
|
36
|
+
'natural+name',
|
|
37
|
+
'tourism+name',
|
|
38
|
+
'leisure+name',
|
|
39
|
+
'historic+name',
|
|
40
|
+
'man_made+name',
|
|
41
|
+
'landuse+name',
|
|
42
|
+
'waterway+name',
|
|
43
|
+
'aerialway+name',
|
|
44
|
+
'craft+name',
|
|
45
|
+
'military+name',
|
|
46
|
+
'aeroway~terminal+name',
|
|
47
|
+
'aeroway~aerodrome+name',
|
|
48
|
+
'aeroway~helipad+name',
|
|
49
|
+
'aeroway~airstrip+name',
|
|
50
|
+
'aeroway~heliport+name',
|
|
51
|
+
'aeroway~areodrome+name',
|
|
52
|
+
'aeroway~spaceport+name',
|
|
53
|
+
'aeroway~landing_strip+name',
|
|
54
|
+
'aeroway~airfield+name',
|
|
55
|
+
'aeroway~airport+name',
|
|
56
|
+
'brand+name',
|
|
57
|
+
'healthcare+name'
|
|
58
|
+
]
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
};
|
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pelias-openstreetmap",
|
|
3
|
-
"version": "8.3.
|
|
3
|
+
"version": "8.3.2",
|
|
4
4
|
"engines": {
|
|
5
5
|
"node": ">=10.0.0"
|
|
6
6
|
},
|
|
7
7
|
"author": "pelias",
|
|
8
8
|
"description": "Pelias openstreetmap utilities",
|
|
9
|
-
"homepage": "https://github.com/
|
|
9
|
+
"homepage": "https://github.com/pelias/openstreetmap",
|
|
10
10
|
"license": "MIT",
|
|
11
11
|
"main": "index.js",
|
|
12
12
|
"scripts": {
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
},
|
|
23
23
|
"repository": {
|
|
24
24
|
"type": "git",
|
|
25
|
-
"url": "https://github.com/
|
|
25
|
+
"url": "https://github.com/pelias/openstreetmap.git"
|
|
26
26
|
},
|
|
27
27
|
"keywords": [
|
|
28
28
|
"pelias",
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"elasticsearch"
|
|
32
32
|
],
|
|
33
33
|
"bugs": {
|
|
34
|
-
"url": "https://github.com/
|
|
34
|
+
"url": "https://github.com/pelias/openstreetmap/issues"
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
37
|
"async": "^3.0.1",
|
package/schema.js
CHANGED
|
@@ -21,7 +21,14 @@ module.exports = Joi.object().keys({
|
|
|
21
21
|
download: Joi.array().items(Joi.object().keys({
|
|
22
22
|
sourceURL: Joi.string()
|
|
23
23
|
}).requiredKeys('sourceURL').unknown(true)),
|
|
24
|
-
deduplicate: Joi.boolean()
|
|
24
|
+
deduplicate: Joi.boolean(),
|
|
25
|
+
addressTags: Joi.array().items(Joi.string()),
|
|
26
|
+
layers: Joi.object().pattern(
|
|
27
|
+
Joi.string(),
|
|
28
|
+
Joi.object().keys({
|
|
29
|
+
tags: Joi.array().items(Joi.string())
|
|
30
|
+
})
|
|
31
|
+
)
|
|
25
32
|
}).requiredKeys('datapath', 'leveldbpath', 'import').unknown(true)
|
|
26
33
|
}).requiredKeys('openstreetmap').unknown(true)
|
|
27
34
|
}).requiredKeys('imports').unknown(true);
|
|
@@ -8,6 +8,11 @@ const through = require('through2');
|
|
|
8
8
|
const Document = require('pelias-model').Document;
|
|
9
9
|
const peliasLogger = require( 'pelias-logger' ).get( 'openstreetmap' );
|
|
10
10
|
const _ = require('lodash');
|
|
11
|
+
const settings = require('pelias-config').generate(require('../schema'));
|
|
12
|
+
const resolveFeatures = require('../util/resolveFeatures');
|
|
13
|
+
const { classify } = require('../util/layerClassifier');
|
|
14
|
+
|
|
15
|
+
const { layers } = resolveFeatures(settings);
|
|
11
16
|
|
|
12
17
|
module.exports = function(){
|
|
13
18
|
|
|
@@ -19,8 +24,10 @@ module.exports = function(){
|
|
|
19
24
|
}
|
|
20
25
|
var uniqueId = [ item.type, item.id ].join('/');
|
|
21
26
|
|
|
22
|
-
//
|
|
23
|
-
|
|
27
|
+
// classify the layer from OSM tags; address_extractor assigns the address
|
|
28
|
+
// layer downstream — the classifier only covers named layers (venue, etc.)
|
|
29
|
+
var layer = classify(item.tags || {}, layers);
|
|
30
|
+
var doc = new Document( 'openstreetmap', layer, uniqueId );
|
|
24
31
|
|
|
25
32
|
// Set latitude / longitude
|
|
26
33
|
if( item.hasOwnProperty('lat') && item.hasOwnProperty('lon') ){
|
package/stream/importPipeline.js
CHANGED
|
@@ -13,6 +13,7 @@ streams.tagMapper = require('./tag_mapper');
|
|
|
13
13
|
streams.addressesWithoutStreet = require('./addresses_without_street');
|
|
14
14
|
streams.adminLookup = require('pelias-wof-admin-lookup').create;
|
|
15
15
|
streams.addressExtractor = require('./address_extractor');
|
|
16
|
+
streams.venueFilter = require('./venueFilter');
|
|
16
17
|
streams.categoryMapper = require('./category_mapper');
|
|
17
18
|
streams.addendumMapper = require('./addendum_mapper');
|
|
18
19
|
streams.popularityMapper = require('./popularity_mapper');
|
|
@@ -26,6 +27,7 @@ streams.import = function(){
|
|
|
26
27
|
.pipe( streams.addressesWithoutStreet() )
|
|
27
28
|
.pipe( streams.tagMapper() )
|
|
28
29
|
.pipe( streams.addressExtractor() )
|
|
30
|
+
.pipe( streams.venueFilter() )
|
|
29
31
|
.pipe( streams.blacklistStream() )
|
|
30
32
|
.pipe( streams.categoryMapper( categoryDefaults ) )
|
|
31
33
|
.pipe( streams.addendumMapper() )
|
package/stream/pbf.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
var fs = require('fs'),
|
|
8
8
|
pbf2json = require('pbf2json'),
|
|
9
9
|
settings = require('pelias-config').generate(require('../schema')),
|
|
10
|
-
|
|
10
|
+
resolveFeatures = require('../util/resolveFeatures'),
|
|
11
11
|
path = require('path');
|
|
12
12
|
|
|
13
13
|
// Create a new pbf parser stream
|
|
@@ -46,11 +46,11 @@ function config(opts){
|
|
|
46
46
|
if(!opts.tags){
|
|
47
47
|
// check if we import venues
|
|
48
48
|
opts.importVenues = settings.imports.openstreetmap.import[0].importVenues;
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
49
|
+
const features = resolveFeatures(settings);
|
|
50
|
+
const layerTags = opts.importVenues === false
|
|
51
|
+
? []
|
|
52
|
+
: Object.values(features.layers).flatMap(l => l.tags ?? []);
|
|
53
|
+
opts.tags = features.addressTags.concat(layerTags);
|
|
54
54
|
}
|
|
55
55
|
return opts;
|
|
56
56
|
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* When importVenues is false, drop any record that is not in the address layer.
|
|
5
|
+
* This prevents named POIs that also have address tags from leaking through as
|
|
6
|
+
* venue records when venue import is disabled.
|
|
7
|
+
*
|
|
8
|
+
* Note: the preferred way to control which layers are imported is now to set
|
|
9
|
+
* `imports.openstreetmap.layers` in pelias.json. Setting `importVenues: false`
|
|
10
|
+
* is equivalent to `layers: {}` and is retained for backward compatibility.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const through = require('through2');
|
|
14
|
+
const settings = require('pelias-config').generate(require('../schema'));
|
|
15
|
+
|
|
16
|
+
module.exports = function() {
|
|
17
|
+
const importVenues = settings.imports.openstreetmap.import[0].importVenues;
|
|
18
|
+
|
|
19
|
+
// passthrough when venues are enabled (default)
|
|
20
|
+
if (importVenues !== false) {
|
|
21
|
+
return through.obj();
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return through.obj(function(doc, enc, next) {
|
|
25
|
+
if (doc.getLayer() === 'address') {
|
|
26
|
+
this.push(doc);
|
|
27
|
+
}
|
|
28
|
+
next();
|
|
29
|
+
});
|
|
30
|
+
};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Test a single pbf2json tag condition against an OSM tags object.
|
|
5
|
+
* Supports 'key' (key must exist) or 'key~value' (key must equal value).
|
|
6
|
+
*/
|
|
7
|
+
function conditionMatchesTags(condition, tags) {
|
|
8
|
+
const tildeIdx = condition.indexOf('~');
|
|
9
|
+
if (tildeIdx === -1) {
|
|
10
|
+
return Object.prototype.hasOwnProperty.call(tags, condition);
|
|
11
|
+
}
|
|
12
|
+
const key = condition.slice(0, tildeIdx);
|
|
13
|
+
const value = condition.slice(tildeIdx + 1);
|
|
14
|
+
return tags[key] === value;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Test whether a full pbf2json pattern string matches a tags object.
|
|
19
|
+
* Pattern conditions are separated by '+' and all must match (AND logic).
|
|
20
|
+
*/
|
|
21
|
+
function patternMatchesTags(pattern, tags) {
|
|
22
|
+
return pattern.split('+').every(cond => conditionMatchesTags(cond, tags));
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Classify an OSM tags object against the features config.
|
|
27
|
+
* Iterates layers in declaration order and returns the first matching layer.
|
|
28
|
+
* Falls back to 'venue' if no patterns match.
|
|
29
|
+
*
|
|
30
|
+
* @param {Object} tags - OSM tags from a pbf2json record
|
|
31
|
+
* @param {Object} features - the features.js keyed-by-layer export
|
|
32
|
+
* @returns {string} layer name
|
|
33
|
+
*/
|
|
34
|
+
function classify(tags, features) {
|
|
35
|
+
for (const [layer, config] of Object.entries(features)) {
|
|
36
|
+
if (Array.isArray(config.tags) && config.tags.some(p => patternMatchesTags(p, tags))) {
|
|
37
|
+
return layer;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return 'venue';
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
module.exports = { patternMatchesTags, classify };
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const defaults = require('../config/features');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Resolve the effective features config by merging pelias.json overrides
|
|
7
|
+
* with the defaults from config/features.js.
|
|
8
|
+
*
|
|
9
|
+
* If `imports.openstreetmap.layers` is set in pelias.json it fully replaces
|
|
10
|
+
* the default layer set. Same for `addressTags`. This lets operators add
|
|
11
|
+
* custom layers (e.g. protected_area) or restrict which tags are extracted
|
|
12
|
+
* without touching the source code.
|
|
13
|
+
*
|
|
14
|
+
* @param {Object} settings - result of pelias-config generate()
|
|
15
|
+
* @returns {{ addressTags: string[], layers: Object }}
|
|
16
|
+
*/
|
|
17
|
+
function resolveFeatures(settings) {
|
|
18
|
+
const osmConfig = (settings && settings.imports && settings.imports.openstreetmap) || {};
|
|
19
|
+
return {
|
|
20
|
+
addressTags: osmConfig.addressTags || defaults.addressTags,
|
|
21
|
+
layers: osmConfig.layers || defaults.layers
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
module.exports = resolveFeatures;
|