]> git.openstreetmap.org Git - nominatim.git/commitdiff
Merge pull request #2932 from lonvia/rework-flex-framework
authorSarah Hoffmann <lonvia@denofr.de>
Thu, 29 Dec 2022 16:23:38 +0000 (17:23 +0100)
committerGitHub <noreply@github.com>
Thu, 29 Dec 2022 16:23:38 +0000 (17:23 +0100)
Switch to osm2pgsql flex output

22 files changed:
CMakeLists.txt
docs/customize/Import-Styles.md
docs/mkdocs.yml
nominatim/config.py
nominatim/tools/exec_utils.py
osm2pgsql
settings/flex-base.lua
settings/import-address.lua [new file with mode: 0644]
settings/import-address.style [deleted file]
settings/import-admin.lua [new file with mode: 0644]
settings/import-admin.style [deleted file]
settings/import-extratags.lua
settings/import-extratags.style [deleted file]
settings/import-full.lua [new file with mode: 0644]
settings/import-full.style [deleted file]
settings/import-street.lua [new file with mode: 0644]
settings/import-street.style [deleted file]
test/bdd/osm2pgsql/import/custom_style.feature [new file with mode: 0644]
test/bdd/osm2pgsql/import/tags.feature
test/bdd/steps/nominatim_environment.py
test/bdd/steps/steps_osm_data.py
test/python/config/test_config.py

index 4fe95f63e03526b385779afcf56acccfbaed3d5c..f151b312e61ff7c5df62823c59022ab97c573cdd 100644 (file)
@@ -271,11 +271,12 @@ endif()
 install(FILES settings/env.defaults
               settings/address-levels.json
               settings/phrase-settings.json
-              settings/import-admin.style
-              settings/import-street.style
-              settings/import-address.style
-              settings/import-full.style
-              settings/import-extratags.style
+              settings/import-admin.lua
+              settings/import-street.lua
+              settings/import-address.lua
+              settings/import-full.lua
+              settings/import-extratags.lua
+              settings/flex-base.lua
               settings/icu_tokenizer.yaml
               settings/country_settings.yaml
         DESTINATION ${NOMINATIM_CONFIGDIR})
index fcd02ae18e3a92bee7a85403f93406ea031b3a82..6085d4e473f0efb4944d2a5eac9c0316f79da733 100644 (file)
 ## Configuring the Import
 
-Which OSM objects are added to the database and which of the tags are used
-can be configured via the import style configuration file. This
-is a JSON file which contains a list of rules which are matched against every
-tag of every object and then assign the tag its specific role.
+In the very first step of a Nominatim import, OSM data is loaded into the
+database. Nominatim uses [osm2pgsql](https://osm2pgsql.org) for this task.
+It comes with a [flex style](https://osm2pgsql.org/doc/manual.html#the-flex-output)
+specifically tailored to filter and convert OSM data into Nominatim's
+internal data representation.
 
-The style to use is given by the `NOMINATIM_IMPORT_STYLE` configuration
-option. There are a number of default styles, which are explained in detail
-in the [Import section](../admin/Import.md#filtering-imported-data). These
-standard styles may be referenced by their name.
+There are a number of default configurations for the flex style which
+result in geocoding databases of different detail. The
+[Import section](../admin/Import.md#filtering-imported-data) explains
+these default configurations in detail.
 
 You can also create your own custom style. Put the style file into your
 project directory and then set `NOMINATIM_IMPORT_STYLE` to the name of the file.
 It is always recommended to start with one of the standard styles and customize
-those. You find the standard styles under the name `import-<stylename>.style`
+those. You find the standard styles under the name `import-<stylename>.lua`
 in the standard Nominatim configuration path (usually `/etc/nominatim` or
 `/usr/local/etc/nominatim`).
 
-The remainder of the page describes the format of the file.
+The remainder of the page describes how the flex style works and how to
+customize it.
 
-### Configuration Rules
+### The `flex-base.lua` module
 
-A single rule looks like this:
+The core of Nominatim's flex import configuration is the `flex-base` module.
+It defines the table layout used by Nominatim and provides standard
+implementations for the import callbacks that make it easy to customize
+how OSM tags are used by Nominatim.
 
-```json
-{
-    "keys" : ["key1", "key2", ...],
-    "values" : {
-        "value1" : "prop",
-        "value2" : "prop1,prop2"
-    }
-}
-```
-
-A rule first defines a list of keys to apply the rule to. This is always a list
-of strings. The string may have four forms. An empty string matches against
-any key. A string that ends in an asterisk `*` is a prefix match and accordingly
-matches against any key that starts with the given string (minus the `*`). A
-suffix match can be defined similarly with a string that starts with a `*`. Any
-other string constitutes an exact match.
+Every custom style should include this module to make sure that the correct
+tables are created. Thus start your custom style as follows:
 
-The second part of the rules defines a list of values and the properties that
-apply to a successful match. Value strings may be either empty, which
-means that they match any value, or describe an exact match. Prefix
-or suffix matching of values is not possible.
+``` lua
+local flex = require('flex-base')
 
-For a rule to match, it has to find a valid combination of keys and values. The
-resulting property is that of the matched values.
+```
 
-The rules in a configuration file are processed sequentially and the first
-match for each tag wins.
+The following sections explain how the module can be customized.
+
+
+### Changing the recognized tags
+
+If you just want to change which OSM tags are recognized during import,
+then there are a number of convenience functions to set the tag lists used
+during the processing.
+
+!!! warning
+    There are no built-in defaults for the tag lists, so all the functions
+    need to be called from your style script to fully process the data.
+    Make sure you start from one of the default style and only modify
+    the data you are interested in. You can also derive your style from an
+    existing style by importing the appropriate module, e.g.
+    `local flex = require('import-street')`.
+
+Many of the following functions take _key match lists_. These lists can
+contain three kinds of strings to match against tag keys:
+A string that ends in an asterisk `*` is a prefix match and accordingly matches
+against any key that starts with the given string (minus the `*`). 
+A suffix match can be defined similarly with a string that starts with a `*`.
+Any other string is matched exactly against tag keys.
+
+
+#### `set_main_tags()` - principal tags
+
+If a principal or main tag is found on an OSM object, then the object
+is included in Nominatim's search index. A single object may also have
+multiple main tags. In that case, the object will be included multiple
+times in the index, once for each main tag.
+
+The flex script distinguishes between four types of main tags:
+
+* __always__: a main tag that is used unconditionally
+* __named__: consider this main tag only, if the object has a proper name
+  (a reference is not enough, see below).
+* __named_with_key__: consider this main tag only, when the object has
+  a proper name with a domain prefix. For example, if the main tag is
+  `bridge=yes`, then it will only be added as an extra row, if there is
+  a tag `bridge:name[:XXX]` for the same object. If this property is set,
+  all other names that are not domain-specific are ignored.
+* __fallback__: use this main tag only, if there is no other main tag.
+  Fallback always implied `named`, i.e. fallbacks are only tried for
+  named objects.
+
+The `set_main_tags()` function takes exactly one table parameter which
+defines the keys and key/value combinations to include and the kind of
+main tag. Each lua table key defines an OSM tag key. The value may
+be a string defining the kind of main key as described above. Then the tag will
+be considered a main tag for any possible value. To further restrict
+which values are acceptable, give a table with the permitted values
+and their kind of main tag. If the table contains a simple value without
+key, then this is used as default for values that are not listed.
+
+!!! example
+    ``` lua
+    local flex = require('import-full')
+
+    flex.set_main_tags{
+        boundary = {administrative = 'named'},
+        highway = {'always', street_lamp = 'named'},
+        landuse = 'fallback'
+    }
+    ```
+
+    In this example an object with a `boundary` tag will only be included
+    when it has a value of `administrative`. Objects with `highway` tags are
+    always included. However when the value is `street_lamp` then the object
+    must have a name, too. With any other value, the object is included
+    independently of the name. Finally, if a `landuse` tag is present then
+    it will be used independely of the concrete value if neither boundary
+    nor highway tags were found and the object is named.
+
+
+#### `set_prefilters()` - ignoring tags
+
+Pre-filtering of tags allows to ignore them for any further processing.
+Thus pre-filtering takes precedence over any other tag processing. This is
+useful when some specific key/value combinations need to be excluded from
+processing. When tags are filtered, they may either be deleted completely
+or moved to `extratags`. Extra tags are saved with the object and returned
+to the user when requested, but are not used otherwise.
+
+`set_prefilters()` takes a table with four optional fields:
+
+* __delete_keys__ is a _key match list_ for tags that should be deleted
+* __delete_tags__ contains a table of tag keys pointing to a list of tag
+  values. Tags with matching key/value pairs are deleted.
+* __extra_keys__ is a _key match list_ for tags which should be saved into
+  extratags
+* __delete_tags__ contains a table of tag keys pointing to a list of tag
+  values. Tags with matching key/value pairs are moved to extratags.
+
+Key list may contain three kinds of strings:
+A string that ends in an asterisk `*` is a prefix match and accordingly matches
+against any key that starts with the given string (minus the `*`). 
+A suffix match can be defined similarly with a string that starts with a `*`.
+Any other string is matched exactly against tag keys.
+
+!!! example
+    ``` lua
+    local flex = require('import-full')
+
+    flex.set_prefilters{
+        delete_keys = {'source', 'source:*'},
+        extra_tags = {amenity = {'yes', 'no'}}
+    }
+    flex.set_main_tags{
+        amenity = 'always'
+    }
+    ```
 
-A rule where key and value are the empty string is special. This defines the
-fallback when none of the rules match. The fallback is always used as a last
-resort when nothing else matches, no matter where the rule appears in the file.
-Defining multiple fallback rules is not allowed. What happens in this case,
-is undefined.
+    In this example any tags `source` and tags that begin with `source:`  are
+    deleted before any other processing is done. Getting rid of frequent tags
+    this way can speed up the import.
 
-### Tag Properties
+    Tags with `amenity=yes` or `amenity=no` are moved to extratags. Later
+    all tags with an `amenity` key are made a main tag. This effectively means
+    that Nominatim will use all amenity tags except for those with value
+    yes and no.
 
-One or more of the following properties may be given for each tag:
+#### `set_name_tags()` - defining names
 
-* `main`
+The flex script distinguishes between two kinds of names:
 
-    A principal tag. A new row will be added for the object with key and value
-    as `class` and `type`.
+* __main__: the primary names make an object fully searchable.
+  Main tags of type _named_ will only cause the object to be included when
+  such a primary name is present. Primary names are usually those found
+  in the `name` tag and its variants.
+* __extra__: extra names are still added to the search index but they are
+  alone not sufficient to make an object named.
 
-* `with_name`
+`set_name_tags()` takes a table with two optional fields `main` and `extra`.
+They take _key match lists_ for main and extra names respectively.
 
-    When the tag is a principal tag (`main` property set): only really add a new
-    row, if there is any name tag found (a reference tag is not sufficient, see
-    below).
+!!! example
+    ``` lua
+    local flex = require('flex-base')
 
-* `with_name_key`
+    flex.set_main_tags{highway = {traffic_light = 'named'}}
+    flex.set_name_tags{main = {'name', 'name:*'},
+                       extra = {'ref'}
+                      }
+    ```
 
-    When the tag is a principal tag (`main` property set): only really add a new
-    row, if there is also a name tag that matches the key of the principal tag.
-    For example, if the main tag is `bridge=yes`, then it will only be added as
-    an extra row, if there is a tag `bridge:name[:XXX]` for the same object.
-    If this property is set, all other names that are not domain-specific are
-    ignored.
+    This example creates a search index over traffic lights but will
+    only include those that have a common name and not those which just
+    have some reference ID from the city.
 
-* `fallback`
+#### `set_address_tags()` - defining address parts
 
-    When the tag is a principal tag (`main` property set): only really add a new
-    row, when no other principal tags for this object have been found. Only one
-    fallback tag can win for an object.
+Address tags will be used to build up the address of an object.
 
-* `operator`
+`set_address_tags()` takes a table with arbitrary fields pointing to
+_key match lists_. To fields have a special meaning:
 
-    When the tag is a principal tag (`main` property set): also include the
-    `operator` tag in the list of names. This is a special construct for an
-    out-dated tagging practise in OSM. Fuel stations and chain restaurants
-    in particular used to have the name of the chain tagged as `operator`.
-    These days the chain can be more commonly found in the `brand` tag but
-    there is still enough old data around to warrant this special case.
+__main__ defines
+the tags that make a full address object out of the OSM object. This
+is usually the housenumber or variants thereof. If a main address tag
+appears, then the object will always be included, if necessary with a
+fallback of `place=house`. If the key has a prefix of `addr:` or `is_in:`
+this will be stripped.
 
-* `name`
+__extra__ defines all supplementary tags for addresses, tags like `addr:street`, `addr:city` etc. If the key has a prefix of `addr:` or `is_in:` this will be stripped.
 
-    Add tag to the list of names.
+All other fields will be handled as summary fields. If a key matches the
+key match list, then its value will be added to the address tags with the
+name of the field as key. If multiple tags match, then an arbitrary one
+wins.
 
-* `ref`
+Country tags are handled slightly special. Only tags with a two-letter code
+are accepted, all other values are discarded.
 
-    Add tag to the list of names as a reference. At the moment this only means
-    that the object is not considered to be named for `with_name`.
+!!! example
+    ``` lua
+    local flex = require('import-full')
 
-* `address`
+    flex.set_address_tags{
+        main = {'addr:housenumber'},
+        extra = {'addr:*'},
+        postcode = {'postal_code', 'postcode', 'addr:postcode'},
+        country = {'country-code', 'ISO3166-1'}
+    }
+    ```
 
-    Add tag to the list of address tags. If the tag starts with `addr:` or
-    `is_in:`, then this prefix is cut off before adding it to the list.
+    In this example all tags which begin with `addr:` will be saved in
+    the address tag list. If one of the tags is `addr:housenumber`, the
+    object will fall back to be entered as a `place=house` in the database
+    unless there is another interested main tag to be found.
 
-* `postcode`
+    Tags with keys `country-code` and `ISO3166-1` are saved with their
+    value under `country` in the address tag list. The same thing happens
+    to postcodes, they will always be saved under the key `postcode` thus
+    normalizing the multitude of keys that are used in the OSM database.
 
-    Add the value as a postcode to the address tags. If multiple tags are
-    candidate for postcodes, one wins out and the others are dropped.
 
-* `country`
+#### `set_unused_handling()` - processing remaining tags
 
-    Add the value as a country code to the address tags. The value must be a
-    two letter country code, otherwise it is ignored. If there are multiple
-    tags that match, then one wins out and the others are dropped.
+This function defines what to do with tags that remain after all tags
+have been classified using the functions above. There are two ways in
+which the function can be used:
 
-* `house`
+`set_unused_handling(delete_keys = ..., delete_tags = ...)` deletes all
+keys that match the descriptions in the parameters and moves all remaining
+tags into the extratags list.
+`set_unused_handling(extra_keys = ..., extra_tags = ...)` moves all tags
+matching the parameters into the extratags list and then deletes the remaining
+tags. For the format of the parameters see the description in `set_prefilters()`
+above.
 
-    If no principle tags can be found for the object, still add the object with
-    `class`=`place` and `type`=`house`. Use this for address nodes that have no
-    other function.
+!!! example
+    ``` lua
+    local flex = require('import-full')
 
-* `interpolation`
+    flex.set_address_tags{
+        main = {'addr:housenumber'},
+        extra = {'addr:*', 'tiger:county'}
+    }
+    flex.set_unused_handling{delete_keys = {'tiger:*'}}
+    ```
 
-    Add this object as an address interpolation (appears as `class`=`place` and
-    `type`=`houses` in the database).
+    In this example all remaining tags except those beginning with `tiger:`
+    are moved to the extratags list. Note that it is not possible to
+    already delete the tiger tags with `set_prefilters()` because that
+    would remove tiger:county before the address tags are processed.
 
-* `extra`
+### Customizing osm2pgsql callbacks
 
-    Add tag to the list of extra tags.
+osm2pgsql expects the flex style to implement three callbacks, one process
+function per OSM type. If you want to implement special handling for
+certain OSM types, you can override the default implementations provided
+by the flex-base module.
 
-* `skip`
+#### Changing the relation types to be handled
 
-    Skip the tag completely. Useful when a custom default fallback is defined
-    or to define exceptions to rules.
+The default scripts only allows relations of type `multipolygon`, `boundary`
+and `waterway`. To add other types relations, set `RELATION_TYPES` for
+the type to the kind of geometry that should be created. The following
+kinds of geometries can be used:
 
-A rule can define as many of these properties for one match as it likes. For
-example, if the property is `"main,extra"` then the tag will open a new row
-but also have the tag appear in the list of extra tags.
+* __relation_as_multipolygon__ creates a (Multi)Polygon from the ways in
+  the relation. If the ways do not form a valid area, then the object is
+  silently discarded.
+* __relation_as_multiline__ creates a (Mutli)LineString from the ways in
+  the relations. Ways are combined as much as possible without any regards
+  to their order in the relation.
+
+!!! Example
+    ``` lua
+    local flex = require('import-full')
+
+    flex.RELATION_TYPES['site'] = flex.relation_as_multipolygon
+    ```
+
+    With this line relations of `type=site` will be included in the index
+    according to main tags found. This only works when the site relation
+    resolves to a valid area. Nodes in the site relation are not part of the
+    geometry.
+
+
+#### Adding additional logic to processing functions
+
+The default processing functions are also exported by the flex-base module
+as `process_node`, `process_way` and `process_relation`. These can be used
+to implement your own processing functions with some additional processing
+logic.
+
+!!! Example
+    ``` lua
+    local flex = require('import-full')
+
+    function osm2pgsql.process_relation(object)
+        if object.tags.boundary ~= 'administrative' or object.tags.admin_level ~= '2' then
+          flex.process_relation(object)
+        end
+    end
+    ```
+
+    This example discards all country-level boundaries and uses standard
+    handling for everything else. This can be useful if you want to use
+    your own custom country boundaries.
+
+
+### Customizing the main processing function
+
+The main processing function of the flex style can be found in the function
+`process_tags`. This function is called for all OSM object kinds and is
+responsible for filtering the tags and writing out the rows into Postgresql.
+
+!!! Example
+    ``` lua
+    local flex = require('import-full')
+
+    local original_process_tags = flex.process_tags
+
+    function flex.process_tags(o)
+        if o.object.tags.highway ~= nil and o.object.tags.access == 'no' then
+            return
+        end
+
+        original_process_tags(o)
+    end
+    ```
+
+    This example shows the most simple customization of the process_tags function.
+    It simply adds some additional processing before running the original code.
+    To do that, first save the original function and then overwrite process_tags
+    from the module. In this example all highways which are not accessible
+    by anyone will be ignored.
+
+
+#### The `Place` class
+
+The `process_tags` function receives a Lua object of `Place` type which comes
+with some handy functions to collect the data necessary for geocoding and
+writing it into the place table. Always use this object to fill the table.
+
+The Place class has some attributes which you may access read-only:
+
+* __object__ is the original OSM object data handed in by osm2pgsql
+* __admin_level__ is the content of the admin_level tag, parsed into an
+  integer and normalized to a value between 0 and 15
+* __has_name__ is a boolean indicating if the object has a full name
+* __names__ is a table with the collected list of name tags
+* __address__ is a table with the collected list of address tags
+* __extratags__ is a table with the collected list of additional tags to save
+
+There are a number of functions to fill these fields. All functions expect
+a table parameter with fields as indicated in the description.
+Many of these functions expect match functions which are described in detail
+further below.
+
+* __delete{match=...}__ removes all tags that match the match function given
+  in _match_.
+* __grab_extratags{match=...}__ moves all tags that match the match function
+  given in _match_ into extratags. Returns the number of tags moved.
+* __clean{delete=..., extra=...}__ deletes all tags that match _delete_ and
+  moves the ones that match _extra_  into extratags
+* __grab_address_parts{groups=...}__ moves matching tags into the address table.
+  _groups_ must be a group match function. Tags of the group `main` and
+  `extra` are added to the address table as is but with `addr:` and `is_in:`
+  prefixes removed from the tag key. All other groups are added with the
+  group name as key and the value from the tag. Multiple values of the same
+  group overwrite each other. The function returns the number of tags saved
+  from the main group.
+* __grab_main_parts{groups=...}__ moves matching tags into the name table.
+  _groups_ must be a group match function. If a tags of the group `main` is
+  present, the object will be marked as having a name. Tags of group `house`
+  produce a fallback to `place=house`. This fallback is return by the function
+  if present.
+
+There are two functions to write a row into the place table. Both functions
+expect the main tag (key and value) for the row and then use the collected
+information from the name, address, extratags etc. fields to complete the row.
+They also have a boolean parameter `save_extra_mains` which defines how any
+unprocessed tags are handled: when True, the tags will be saved as extratags,
+when False, they will be simply discarded.
+
+* __write_row(key, value, save_extra_mains)__ creates a new table row from
+  the current state of the Place object.
+* __write_place(key, value, mtype, save_extra_mains)__ creates a new row
+  conditionally. When value is nil, the function will attempt to look up the
+  value in the object tags. If value is still nil or mtype is nil, the row
+  is ignored. An mtype of `always` will then always write out the row,
+  a mtype of `named` only, when the object has a full name. When mtype
+  is `named_with_key`, the function checks for a domain name, i.e. a name
+  tag prefixed with the name of the main key. Only if at least one is found,
+  the row will be written. The names are replaced with the domain names found.
+
+#### Match functions
+
+The Place functions usually expect either a _match function_ or a
+_group match function_ to find the tags to apply their function to.
+
+The __match function__ is a Lua function which takes two parameters,
+key and value, and returns a boolean to indicate that a tag matches. The
+flex-base module has a convenience function `tag_match()` to create such a
+function. It takes a table with two optional fields: `keys` takes a key match
+list (see above), `tags` takes a table with keys that point to a list of
+possible values, thus defining key/value matches.
+
+The __group match function__ is a Lua function which also takes two parameters,
+key and value, and returns a string indicating to which group or type they
+belong to. The `tag_group()` can be used to create such a function. It expects
+a table where the group names are the keys and the values are a key match list.
+
+
+
+### Using the gazetteer output of osm2pgsql
+
+Nominatim still allows you to configure the gazetteer output to remain
+backwards compatible with older imports. It will be automatically used
+when the style file name ends in `.style`. For documentation of the
+old import style, please refer to the documentation of older releases
+of Nominatim. Do not use the gazetteer output for new imports. There is no
+guarantee that new versions of Nominatim are fully compatible with the
+gazetteer output.
 
 ### Changing the Style of Existing Databases
 
index ab7dec30508838bc9e2c56c094686e8d186140cc..eb624d8bd18973b67269e9f6bf7cfe5f4e9abbc9 100644 (file)
@@ -51,6 +51,7 @@ nav:
 markdown_extensions:
     - codehilite
     - admonition
+    - pymdownx.superfences
     - def_list
     - toc:
         permalink: 
index 1728c291e92fadec129f8a531247165dbea688ee..e0f19b04eaddf7a1dcee168fc0ed87aad23350ae 100644 (file)
@@ -172,7 +172,7 @@ class Configuration:
         style = getattr(self, 'IMPORT_STYLE')
 
         if style in ('admin', 'street', 'address', 'full', 'extratags'):
-            return self.config_dir / f'import-{style}.style'
+            return self.config_dir / f'import-{style}.lua'
 
         return self.find_config_file('', 'IMPORT_STYLE')
 
index 2e5c4801606115f1fe3adfa6616bb39d241ab5f3..ab2ccc7cf9d83d4047cd5a7f5b7c14ba634bb29a 100644 (file)
@@ -125,7 +125,7 @@ def run_osm2pgsql(options: Mapping[str, Any]) -> None:
           ]
 
     if str(options['osm2pgsql_style']).endswith('.lua'):
-        env['LUA_PATH'] = ';'.join((str(options['osm2pgsql_style_path'] / 'flex-base.lua'),
+        env['LUA_PATH'] = ';'.join((str(options['osm2pgsql_style_path'] / '?.lua'),
                                     os.environ.get('LUAPATH', ';')))
         cmd.extend(('--output', 'flex'))
     else:
index 6a5d2500e9689f55485d186306aadc55560085fd..4facd1aea451cea220261c361698b8e5f18a9327 160000 (submodule)
--- a/osm2pgsql
+++ b/osm2pgsql
@@ -1 +1 @@
-Subproject commit 6a5d2500e9689f55485d186306aadc55560085fd
+Subproject commit 4facd1aea451cea220261c361698b8e5f18a9327
index fe3ce32ae6d85cd18fe0217900ba324da58c12e8..0e11273633cfc6dabcda67b1f3f1f50954527fe8 100644 (file)
@@ -1,9 +1,19 @@
 -- Core functions for Nominatim import flex style.
 --
 
+local module = {}
+
+local PRE_DELETE = nil
+local PRE_EXTRAS = nil
+local MAIN_KEYS = nil
+local NAMES = nil
+local ADDRESS_TAGS = nil
+local SAVE_EXTRA_MAINS = false
+local POSTCODE_FALLBACK = true
+
 
 -- The single place table.
-place_table = osm2pgsql.define_table{
+local place_table = osm2pgsql.define_table{
     name = "place",
     ids = { type = 'any', id_column = 'osm_id', type_column = 'osm_type' },
     columns = {
@@ -14,7 +24,25 @@ place_table = osm2pgsql.define_table{
         { column = 'address', type = 'hstore' },
         { column = 'extratags', type = 'hstore' },
         { column = 'geometry', type = 'geometry', projection = 'WGS84', not_null = true },
-    }
+    },
+    indexes = {}
+}
+
+------------ Geometry functions for relations ---------------------
+
+function module.relation_as_multipolygon(o)
+    return o:as_multipolygon()
+end
+
+function module.relation_as_multiline(o)
+    return o:as_multilinestring():line_merge()
+end
+
+
+module.RELATION_TYPES = {
+    multipolygon = module.relation_as_multipolygon,
+    boundary = module.relation_as_multipolygon,
+    waterway = module.relation_as_multiline
 }
 
 ------------- Place class ------------------------------------------
@@ -43,6 +71,17 @@ function Place.new(object, geom_func)
     return self
 end
 
+function Place:clean(data)
+    for k, v in pairs(self.object.tags) do
+        if data.delete ~= nil and data.delete(k, v) then
+            self.object.tags[k] = nil
+        elseif data.extra ~= nil and data.extra(k, v) then
+            self.extratags[k] = v
+            self.object.tags[k] = nil
+        end
+    end
+end
+
 function Place:delete(data)
     if data.match ~= nil then
         for k, v in pairs(self.object.tags) do
@@ -69,31 +108,37 @@ function Place:grab_extratags(data)
     return count
 end
 
-function Place:grab_address(data)
+local function strip_address_prefix(k)
+    if k:sub(1, 5) == 'addr:' then
+        return k:sub(6)
+    end
+
+    if k:sub(1, 6) == 'is_in:' then
+        return k:sub(7)
+    end
+
+    return k
+end
+
+
+function Place:grab_address_parts(data)
     local count = 0
 
-    if data.match ~= nil then
+    if data.groups ~= nil then
         for k, v in pairs(self.object.tags) do
-            if data.match(k, v) then
-                self.object.tags[k] = nil
+            local atype = data.groups(k, v)
 
-                if data.include_on_name == true then
+            if atype ~= nil then
+                if atype == 'main' then
                     self.has_name = true
-                end
-
-                if data.out_key ~= nil then
-                    self.address[data.out_key] = v
-                    return 1
-                end
-
-                if k:sub(1, 5) == 'addr:' then
-                    self.address[k:sub(6)] = v
-                elseif k:sub(1, 6) == 'is_in:' then
-                    self.address[k:sub(7)] = v
+                    self.address[strip_address_prefix(k)] = v
+                    count = count + 1
+                elseif atype == 'extra' then
+                    self.address[strip_address_prefix(k)] = v
                 else
-                    self.address[k] = v
+                    self.address[atype] = v
                 end
-                count = count + 1
+                self.object.tags[k] = nil
             end
         end
     end
@@ -101,36 +146,30 @@ function Place:grab_address(data)
     return count
 end
 
-function Place:set_address(key, value)
-    self.address[key] = value
-end
 
-function Place:grab_name(data)
-    local count = 0
+function Place:grab_name_parts(data)
+    local fallback = nil
 
-    if data.match ~= nil then
+    if data.groups ~= nil then
         for k, v in pairs(self.object.tags) do
-            if data.match(k, v) then
-                self.object.tags[k] = nil
+            local atype = data.groups(k, v)
+
+            if atype ~= nil then
                 self.names[k] = v
-                if data.include_on_name ~= false then
+                self.object.tags[k] = nil
+                if atype == 'main' then
                     self.has_name = true
+                elseif atype == 'house' then
+                    self.has_name = true
+                    fallback = {'place', 'house', 'always'}
                 end
-                count = count + 1
             end
         end
     end
 
-    return count
-end
-
-function Place:grab_tag(key)
-    return self.object:grab_tag(key)
+    return fallback
 end
 
-function Place:tags()
-    return self.object.tags
-end
 
 function Place:write_place(k, v, mtype, save_extra_mains)
     if mtype == nil then
@@ -214,7 +253,7 @@ function Place:write_row(k, v, save_extra_mains)
 end
 
 
-function tag_match(data)
+function module.tag_match(data)
     if data == nil or next(data) == nil then
         return nil
     end
@@ -276,17 +315,72 @@ function tag_match(data)
 end
 
 
+function module.tag_group(data)
+    if data == nil or next(data) == nil then
+        return nil
+    end
+
+    local fullmatches = {}
+    local key_prefixes = {}
+    local key_suffixes = {}
+
+    for group, tags in pairs(data) do
+        for _, key in pairs(tags) do
+            if key:sub(1, 1) == '*' then
+                if #key > 1 then
+                    if key_suffixes[#key - 1] == nil then
+                        key_suffixes[#key - 1] = {}
+                    end
+                    key_suffixes[#key - 1][key:sub(2)] = group
+                end
+            elseif key:sub(#key, #key) == '*' then
+                if key_prefixes[#key - 1] == nil then
+                    key_prefixes[#key - 1] = {}
+                end
+                key_prefixes[#key - 1][key:sub(1, #key - 1)] = group
+            else
+                fullmatches[key] = group
+            end
+        end
+    end
+
+    return function (k, v)
+        local val = fullmatches[k]
+        if val ~= nil then
+            return val
+        end
+
+        for slen, slist in pairs(key_suffixes) do
+            if #k >= slen then
+                val = slist[k:sub(-slen)]
+                if val ~= nil then
+                    return val
+                end
+            end
+        end
+
+        for slen, slist in pairs(key_prefixes) do
+            if #k >= slen then
+                val = slist[k:sub(1, slen)]
+                if val ~= nil then
+                    return val
+                end
+            end
+        end
+    end
+end
+
 -- Process functions for all data types
-function osm2pgsql.process_node(object)
+function module.process_node(object)
 
     local function geom_func(o)
         return o:as_point()
     end
 
-    process_tags(Place.new(object, geom_func))
+    module.process_tags(Place.new(object, geom_func))
 end
 
-function osm2pgsql.process_way(object)
+function module.process_way(object)
 
     local function geom_func(o)
         local geom = o:as_polygon()
@@ -298,30 +392,24 @@ function osm2pgsql.process_way(object)
         return geom
     end
 
-    process_tags(Place.new(object, geom_func))
-end
-
-function relation_as_multipolygon(o)
-    return o:as_multipolygon()
-end
-
-function relation_as_multiline(o)
-    return o:as_multilinestring():line_merge()
+    module.process_tags(Place.new(object, geom_func))
 end
 
-function osm2pgsql.process_relation(object)
-    local geom_func = RELATION_TYPES[object.tags.type]
+function module.process_relation(object)
+    local geom_func = module.RELATION_TYPES[object.tags.type]
 
     if geom_func ~= nil then
-        process_tags(Place.new(object, geom_func))
+        module.process_tags(Place.new(object, geom_func))
     end
 end
 
-function process_tags(o)
-    local fallback
+-- The process functions are used by default by osm2pgsql.
+osm2pgsql.process_node = module.process_node
+osm2pgsql.process_way = module.process_way
+osm2pgsql.process_relation = module.process_relation
 
-    o:delete{match = PRE_DELETE}
-    o:grab_extratags{match = PRE_EXTRAS}
+function module.process_tags(o)
+    o:clean{delete = PRE_DELETE, extra = PRE_EXTRAS}
 
     -- Exception for boundary/place double tagging
     if o.object.tags.boundary == 'administrative' then
@@ -330,54 +418,93 @@ function process_tags(o)
         end}
     end
 
+    -- name keys
+    local fallback = o:grab_name_parts{groups=NAMES}
+
     -- address keys
-    o:grab_address{match=COUNTRY_TAGS, out_key='country'}
+    if o:grab_address_parts{groups=ADDRESS_TAGS} > 0 and fallback == nil then
+        fallback = {'place', 'house', 'always'}
+    end
     if o.address.country ~= nil and #o.address.country ~= 2 then
         o.address['country'] = nil
     end
-    if o:grab_name{match=HOUSENAME_TAGS} > 0 then
-        fallback = {'place', 'house'}
-    end
-    if o:grab_address{match=HOUSENUMBER_TAGS, include_on_name = true} > 0 and fallback == nil then
-        fallback = {'place', 'house'}
+    if POSTCODE_FALLBACK and fallback == nil and o.address.postcode ~= nil then
+        fallback = {'place', 'postcode', 'always'}
     end
-    if o:grab_address{match=POSTCODES, out_key='postcode'} > 0 and fallback == nil then
-        fallback = {'place', 'postcode'}
+
+    if o.address.interpolation ~= nil then
+        o:write_place('place', 'houses', 'always', SAVE_EXTRA_MAINS)
+        return
     end
 
-    local is_interpolation = o:grab_address{match=INTERPOLATION_TAGS} > 0
+    o:clean{delete = POST_DELETE, extra = POST_EXTRAS}
 
-    o:grab_address{match=ADDRESS_TAGS}
+    -- collect main keys
+    for k, v in pairs(o.object.tags) do
+        local ktype = MAIN_KEYS[k]
+        if ktype == 'fallback' then
+            if o.has_name then
+                fallback = {k, v, 'named'}
+            end
+        elseif ktype ~= nil then
+            o:write_place(k, v, MAIN_KEYS[k], SAVE_EXTRA_MAINS)
+        end
+    end
 
-    if is_interpolation then
-        o:write_place('place', 'houses', 'always', SAVE_EXTRA_MAINS)
-        return
+    if fallback ~= nil and o.num_entries == 0 then
+        o:write_place(fallback[1], fallback[2], fallback[3], SAVE_EXTRA_MAINS)
     end
+end
 
-    -- name keys
-    o:grab_name{match = NAMES}
-    o:grab_name{match = REFS, include_on_name = false}
+--------- Convenience functions for simple style configuration -----------------
 
-    o:delete{match = POST_DELETE}
-    o:grab_extratags{match = POST_EXTRAS}
 
-    -- collect main keys
-    local num_mains = 0
-    for k, v in pairs(o:tags()) do
-        num_mains = num_mains + o:write_place(k, v, MAIN_KEYS[k], SAVE_EXTRA_MAINS)
+function module.set_prefilters(data)
+    PRE_DELETE = module.tag_match{keys = data.delete_keys, tags = data.delete_tags}
+    PRE_EXTRAS = module.tag_match{keys = data.extra_keys,
+                                  tags = data.extra_tags}
+end
+
+function module.set_main_tags(data)
+    MAIN_KEYS = data
+end
+
+function module.set_name_tags(data)
+    NAMES = module.tag_group(data)
+end
+
+function module.set_address_tags(data)
+    if data.postcode_fallback ~= nil then
+        POSTCODE_FALLBACK = data.postcode_fallback
+        data.postcode_fallback = nil
     end
 
-    if num_mains == 0 then
-        for tag, mtype in pairs(MAIN_FALLBACK_KEYS) do
-            if o:write_place(tag, nil, mtype, SAVE_EXTRA_MAINS) > 0 then
-                return
-            end
-        end
+    ADDRESS_TAGS = module.tag_group(data)
+end
 
-        if fallback ~= nil then
-            o:write_place(fallback[1], fallback[2], 'always', SAVE_EXTRA_MAINS)
-        end
+function module.set_unused_handling(data)
+    if data.extra_keys == nil and data.extra_tags == nil then
+        POST_DELETE = module.tag_match{keys = data.delete_keys, tags = data.delete_tags}
+        POST_EXTRAS = nil
+        SAVE_EXTRA_MAINS = true
+    elseif data.delete_keys == nil and data.delete_tags == nil then
+        POST_DELETE = nil
+        POST_EXTRAS = module.tag_match{keys = data.extra_keys, tags = data.extra_tags}
+        SAVE_EXTRA_MAINS = false
+    else
+        error("unused handler can have only 'extra_keys' or 'delete_keys' set.")
     end
 end
 
+function set_relation_types(data)
+    module.RELATION_TYPES = {}
+    for k, v in data do
+        if v == 'multipolygon' then
+            module.RELATION_TYPES[k] = module.relation_as_multipolygon
+        elseif v == 'multiline' then
+            module.RELATION_TYPES[k] = module.relation_as_multiline
+        end
+    end
+end
 
+return module
diff --git a/settings/import-address.lua b/settings/import-address.lua
new file mode 100644 (file)
index 0000000..00d089c
--- /dev/null
@@ -0,0 +1,69 @@
+local flex = require('flex-base')
+
+flex.set_main_tags{
+    highway = {'always',
+               street_lamp = 'named',
+               traffic_signals = 'named',
+               service = 'named',
+               cycleway = 'named',
+               path = 'named',
+               footway = 'named',
+               steps = 'named',
+               bridleway = 'named',
+               track = 'named',
+               motorway_link = 'named',
+               trunk_link = 'named',
+               primary_link = 'named',
+               secondary_link = 'named',
+               tertiary_link = 'named'},
+    boundary = {administrative = 'named',
+                postal_code = 'named'},
+    landuse = 'fallback',
+    place = 'always'
+}
+
+flex.set_prefilters{delete_keys = {'building', 'source',
+                                   'source', '*source', 'type',
+                                   'is_in:postcode', '*:wikidata',
+                                   '*:prefix', '*:suffix', 'name:prefix:*', 'name:suffix:*',
+                                   'name:etymology', 'name:signed', 'name:botanical',
+                                   'addr:street:name', 'addr:street:type'},
+                    delete_tags = {highway = {'no', 'turning_circle', 'mini_roundabout',
+                                              'noexit', 'crossing', 'give_way', 'stop'},
+                                   landuse = {'cemetry', 'no'},
+                                   boundary = {'place'}},
+                    extra_keys = {'wikipedia', 'wikipedia:*', 'wikidata', 'capital', 'area'}
+                   }
+
+flex.set_name_tags{main = {'name', 'name:*',
+                          'int_name', 'int_name:*',
+                          'nat_name', 'nat_name:*',
+                          'reg_name', 'reg_name:*',
+                          'loc_name', 'loc_name:*',
+                          'old_name', 'old_name:*',
+                          'alt_name', 'alt_name:*', 'alt_name_*',
+                          'official_name', 'official_name:*',
+                          'place_name', 'place_name:*',
+                          'short_name', 'short_name:*', 'brand'},
+                   extra = {'ref', 'int_ref', 'nat_ref', 'reg_ref',
+                            'loc_ref', 'old_ref',
+                            'iata', 'icao', 'pcode', 'pcode:*', 'ISO3166-2'},
+                   house = {'addr:housename'}
+                  }
+
+flex.set_address_tags{main = {'addr:housenumber',
+                              'addr:conscriptionnumber',
+                              'addr:streetnumber'},
+                      extra = {'addr:*', 'is_in:*', 'tiger:county'},
+                      postcode = {'postal_code', 'postcode', 'addr:postcode',
+                                  'tiger:zip_left', 'tiger:zip_right'},
+                      country = {'country_code', 'ISO3166-1',
+                                 'addr:country_code', 'is_in:country_code',
+                                 'addr:country', 'is_in:country'},
+                      interpolation = {'addr:interpolation'}
+                     }
+
+
+flex.set_unused_handling{extra_keys = {'place'}}
+
+return flex
diff --git a/settings/import-address.style b/settings/import-address.style
deleted file mode 100644 (file)
index b4457fa..0000000
+++ /dev/null
@@ -1,124 +0,0 @@
-[
-{
-    "keys" : [ "" ],
-    "values" : {
-        "no" : "skip"
-    }
-},
-{   "keys" : ["wikipedia", "wikipedia:*", "wikidata", "area"],
-    "values" : {
-        "" : "extra"
-    }
-},
-{
-    "keys" : ["*:prefix", "*:suffix", "name:prefix:*", "name:suffix:*",
-              "name:etymology", "name:signed", "name:botanical", "*:wikidata",
-              "addr:street:name", "addr:street:type"],
-    "values" : {
-        "" : "skip"
-    }
-},
-{
-    "keys" : ["ref", "int_ref", "nat_ref", "reg_ref", "loc_ref", "old_ref",
-              "iata", "icao", "pcode", "ISO3166-2"],
-    "values" : {
-        "" : "ref"
-    }
-},
-{
-    "keys" : ["name", "name:*", "int_name", "int_name:*", "nat_name", "nat_name:*",
-              "reg_name", "reg_name:*", "loc_name", "loc_name:*",
-              "old_name", "old_name:*", "alt_name", "alt_name:*", "alt_name_*",
-              "official_name", "official_name:*", "place_name", "place_name:*",
-              "short_name", "short_name:*", "brand"],
-    "values" : {
-        "" : "name"
-    }
-},
-{
-    "keys" : ["landuse"],
-    "values" : {
-        "cemetry" : "skip",
-        "" : "fallback,with_name"
-    }
-},
-{
-    "keys" : ["boundary"],
-    "values" : {
-        "administrative" : "main",
-        "postal_code" : "main"
-    }
-},
-{
-    "keys" : ["place"],
-    "values" : {
-        "" : "main"
-    }
-},
-{
-    "keys" : ["addr:housename"],
-    "values" : {
-        "" : "name,house"
-    }
-},
-{
-    "keys" : ["addr:housenumber", "addr:conscriptionnumber", "addr:streetnumber"],
-    "values" : {
-        "" : "address,house"
-    }
-},
-{
-    "keys" : ["addr:interpolation"],
-    "values" : {
-        "" : "interpolation,address"
-    }
-},
-{
-    "keys" : ["postal_code", "postcode", "addr:postcode",
-              "tiger:zip_left", "tiger:zip_right"],
-    "values" : {
-        "" : "postcode,fallback"
-    }
-},
-{
-    "keys" : ["country_code", "ISO3166-1", "is_in:country_code", "is_in:country",
-              "addr:country", "addr:country_code"],
-    "values" : {
-        "" : "country"
-    }
-},
-{
-    "keys" : ["addr:*", "is_in:*", "tiger:county"],
-    "values" : {
-        "" : "address"
-    }
-},
-{
-    "keys" : ["highway"],
-    "values" : {
-        "motorway" : "main",
-        "trunk" : "main",
-        "primary" : "main",
-        "secondary" : "main",
-        "tertiary" : "main",
-        "unclassified" : "main",
-        "residential" : "main",
-        "living_street" : "main",
-        "pedestrian" : "main",
-        "road" : "main",
-        "service" : "main,with_name",
-        "cycleway" : "main,with_name",
-        "path" : "main,with_name",
-        "footway" : "main,with_name",
-        "steps" : "main,with_name",
-        "bridleway" : "main,with_name",
-        "track" : "main,with_name",
-        "byway": "main,with_name",
-        "motorway_link" : "main,with_name",
-        "trunk_link" : "main,with_name",
-        "primary_link" : "main,with_name",
-        "secondary_link" : "main,with_name",
-        "tertiary_link" : "main,with_name"
-    }
-}
-]
diff --git a/settings/import-admin.lua b/settings/import-admin.lua
new file mode 100644 (file)
index 0000000..a1164ac
--- /dev/null
@@ -0,0 +1,46 @@
+local flex = require('flex-base')
+
+flex.set_main_tags{
+    boundary = {administrative = 'named'},
+    landuse = 'fallback',
+    place = 'always'
+}
+
+flex.set_prefilters{delete_keys = {'building', 'source', 'highway',
+                                   'addr:housenumber', 'addr:street', 'addr:city',
+                                   'source', '*source', 'type',
+                                   'is_in:postcode', '*:wikidata',
+                                   '*:prefix', '*:suffix', 'name:prefix:*', 'name:suffix:*',
+                                   'name:etymology', 'name:signed', 'name:botanical',
+                                   'addr:street:name', 'addr:street:type'},
+                    delete_tags = {landuse = {'cemetry', 'no'},
+                                   boundary = {'place'}},
+                    extra_keys = {'wikipedia', 'wikipedia:*', 'wikidata', 'capital'}
+                   }
+
+flex.set_name_tags{main = {'name', 'name:*',
+                          'int_name', 'int_name:*',
+                          'nat_name', 'nat_name:*',
+                          'reg_name', 'reg_name:*',
+                          'loc_name', 'loc_name:*',
+                          'old_name', 'old_name:*',
+                          'alt_name', 'alt_name:*', 'alt_name_*',
+                          'official_name', 'official_name:*',
+                          'place_name', 'place_name:*',
+                          'short_name', 'short_name:*', 'brand'},
+                   extra = {'ref', 'int_ref', 'nat_ref', 'reg_ref',
+                            'loc_ref', 'old_ref',
+                            'iata', 'icao', 'pcode', 'pcode:*', 'ISO3166-2'}
+                  }
+
+flex.set_address_tags{extra = {'addr:*', 'is_in:*'},
+                      postcode = {'postal_code', 'postcode', 'addr:postcode'},
+                      country = {'country_code', 'ISO3166-1',
+                                 'addr:country_code', 'is_in:country_code',
+                                 'addr:country', 'is_in:country'},
+                      postcode_fallback = false
+                     }
+
+flex.set_unused_handling{extra_keys = {'place'}}
+
+return flex
diff --git a/settings/import-admin.style b/settings/import-admin.style
deleted file mode 100644 (file)
index 37d03a8..0000000
+++ /dev/null
@@ -1,77 +0,0 @@
-[
-{   "keys" : ["wikipedia", "wikipedia:*", "wikidata"],
-    "values" : {
-        "" : "extra"
-    }
-},
-{
-    "keys" : ["*:prefix", "*:suffix", "name:prefix:*", "name:suffix:*",
-              "name:etymology", "name:signed", "name:botanical", "*:wikidata",
-              "addr:street:name", "addr:street:type"],
-    "values" : {
-        "" : "skip"
-    }
-},
-{
-    "keys" : ["ref", "int_ref", "nat_ref", "reg_ref", "loc_ref", "old_ref",
-              "iata", "icao", "pcode", "ISO3166-2"],
-    "values" : {
-        "" : "ref"
-    }
-},
-{
-    "keys" : ["name", "name:*", "int_name", "int_name:*", "nat_name", "nat_name:*",
-              "reg_name", "reg_name:*", "loc_name", "loc_name:*",
-              "old_name", "old_name:*", "alt_name", "alt_name:*", "alt_name_*",
-              "official_name", "official_name:*", "place_name", "place_name:*",
-              "short_name", "short_name:*", "brand"],
-    "values" : {
-        "" : "name"
-    }
-},
-{
-    "keys" : ["landuse"],
-    "values" : {
-        "cemetry" : "skip",
-        "" : "fallback,with_name"
-    }
-},
-{
-    "keys" : ["boundary"],
-    "values" : {
-        "administrative" : "main"
-    }
-},
-{
-    "keys" : ["place"],
-    "values" : {
-        "" : "main"
-    }
-},
-{
-    "keys" : ["country_code", "ISO3166-1", "is_in:country_code", "is_in:country",
-              "addr:country", "addr:country_code"],
-    "values" : {
-        "" : "country"
-    }
-},
-{
-    "keys" : ["addr:*", "is_in:*", "tiger:county"],
-    "values" : {
-        "" : "address"
-    }
-},
-{
-    "keys" : ["postal_code", "postcode", "addr:postcode",
-              "tiger:zip_left", "tiger:zip_right"],
-    "values" : {
-        "" : "postcode"
-    }
-},
-{
-    "keys" : ["capital"],
-    "values" : {
-        "" : "extra"
-    }
-}
-]
index 7b1880ef7df4486a417e362d4df8a0ff41c626fc..d634d4a1ba9d0625aef597a39ef746427e9621f0 100644 (file)
@@ -1,13 +1,9 @@
-require('flex-base')
+local flex = require('flex-base')
 
-RELATION_TYPES = {
-    multipolygon = relation_as_multipolygon,
-    boundary = relation_as_multipolygon,
-    waterway = relation_as_multiline
-}
-
-MAIN_KEYS = {
+flex.set_main_tags{
+    building = 'fallback',
     emergency = 'always',
+    healthcare = 'fallback',
     historic = 'always',
     military = 'always',
     natural = 'named',
@@ -36,6 +32,8 @@ MAIN_KEYS = {
     amenity = 'always',
     club = 'always',
     craft = 'always',
+    junction = 'fallback',
+    landuse = 'fallback',
     leisure = 'always',
     office = 'always',
     mountain_pass = 'always',
@@ -47,55 +45,42 @@ MAIN_KEYS = {
     place = 'always'
 }
 
-MAIN_FALLBACK_KEYS = {
-    building = 'named',
-    landuse = 'named',
-    junction = 'named',
-    healthcare = 'named'
-}
-
-
-PRE_DELETE = tag_match{keys = {'note', 'note:*', 'source', 'source*', 'attribution',
-                               'comment', 'fixme', 'FIXME', 'created_by', 'NHD:*',
-                               'nhd:*', 'gnis:*', 'geobase:*', 'KSJ2:*', 'yh:*',
-                               'osak:*', 'naptan:*', 'CLC:*', 'import', 'it:fvg:*',
-                               'type', 'lacounty:*', 'ref:ruian:*', 'building:ruian:type',
-                               'ref:linz:*', 'is_in:postcode'},
-                       tags = {emergency = {'yes', 'no', 'fire_hydrant'},
-                               historic = {'yes', 'no'},
-                               military = {'yes', 'no'},
-                               natural = {'yes', 'no', 'coastline'},
-                               highway = {'no', 'turning_circle', 'mini_roundabout',
-                                          'noexit', 'crossing', 'give_way', 'stop'},
-                               railway = {'level_crossing', 'no', 'rail'},
-                               man_made = {'survey_point', 'cutline'},
-                               aerialway = {'pylon', 'no'},
-                               aeroway = {'no'},
-                               amenity = {'no'},
-                               club = {'no'},
-                               craft = {'no'},
-                               leisure = {'no'},
-                               office = {'no'},
-                               mountain_pass = {'no'},
-                               shop = {'no'},
-                               tourism = {'yes', 'no'},
-                               bridge = {'no'},
-                               tunnel = {'no'},
-                               waterway = {'riverbank'},
-                               building = {'no'},
-                               boundary = {'place'}}
-                      }
-
-POST_DELETE = tag_match{keys = {'tiger:*'}}
-
-PRE_EXTRAS = tag_match{keys = {'*:prefix', '*:suffix', 'name:prefix:*', 'name:suffix:*',
+flex.set_prefilters{delete_keys = {'note', 'note:*', 'source', '*source', 'attribution',
+                                   'comment', 'fixme', 'FIXME', 'created_by', 'NHD:*',
+                                   'nhd:*', 'gnis:*', 'geobase:*', 'KSJ2:*', 'yh:*',
+                                   'osak:*', 'naptan:*', 'CLC:*', 'import', 'it:fvg:*',
+                                   'type', 'lacounty:*', 'ref:ruian:*', 'building:ruian:type',
+                                   'ref:linz:*', 'is_in:postcode'},
+                    delete_tags = {emergency = {'yes', 'no', 'fire_hydrant'},
+                                   historic = {'yes', 'no'},
+                                   military = {'yes', 'no'},
+                                   natural = {'yes', 'no', 'coastline'},
+                                   highway = {'no', 'turning_circle', 'mini_roundabout',
+                                              'noexit', 'crossing', 'give_way', 'stop'},
+                                   railway = {'level_crossing', 'no', 'rail'},
+                                   man_made = {'survey_point', 'cutline'},
+                                   aerialway = {'pylon', 'no'},
+                                   aeroway = {'no'},
+                                   amenity = {'no'},
+                                   club = {'no'},
+                                   craft = {'no'},
+                                   leisure = {'no'},
+                                   office = {'no'},
+                                   mountain_pass = {'no'},
+                                   shop = {'no'},
+                                   tourism = {'yes', 'no'},
+                                   bridge = {'no'},
+                                   tunnel = {'no'},
+                                   waterway = {'riverbank'},
+                                   building = {'no'},
+                                   boundary = {'place'}},
+                    extra_keys = {'*:prefix', '*:suffix', 'name:prefix:*', 'name:suffix:*',
                                'name:etymology', 'name:signed', 'name:botanical',
                                'wikidata', '*:wikidata',
                                'addr:street:name', 'addr:street:type'}
-                      }
+                   }
 
-
-NAMES = tag_match{keys = {'name', 'name:*',
+flex.set_name_tags{main = {'name', 'name:*',
                           'int_name', 'int_name:*',
                           'nat_name', 'nat_name:*',
                           'reg_name', 'reg_name:*',
@@ -104,26 +89,26 @@ NAMES = tag_match{keys = {'name', 'name:*',
                           'alt_name', 'alt_name:*', 'alt_name_*',
                           'official_name', 'official_name:*',
                           'place_name', 'place_name:*',
-                          'short_name', 'short_name:*', 'brand'}}
-
-REFS = tag_match{keys = {'ref', 'int_ref', 'nat_ref', 'reg_ref', 'loc_ref', 'old_ref',
-                         'iata', 'icao', 'pcode', 'pcode:*', 'ISO3166-2'}}
-
-POSTCODES = tag_match{keys = {'postal_code', 'postcode', 'addr:postcode',
-                              'tiger:zip_left', 'tiger:zip_right'}}
-
-COUNTRY_TAGS = tag_match{keys = {'country_code', 'ISO3166-1',
+                          'short_name', 'short_name:*', 'brand'},
+                   extra = {'ref', 'int_ref', 'nat_ref', 'reg_ref',
+                            'loc_ref', 'old_ref',
+                            'iata', 'icao', 'pcode', 'pcode:*', 'ISO3166-2'},
+                   house = {'addr:housename'}
+                  }
+
+flex.set_address_tags{main = {'addr:housenumber',
+                              'addr:conscriptionnumber',
+                              'addr:streetnumber'},
+                      extra = {'addr:*', 'is_in:*', 'tiger:county'},
+                      postcode = {'postal_code', 'postcode', 'addr:postcode',
+                                  'tiger:zip_left', 'tiger:zip_right'},
+                      country = {'country_code', 'ISO3166-1',
                                  'addr:country_code', 'is_in:country_code',
-                                 'addr:country', 'is_in:country'}}
-
-HOUSENAME_TAGS = tag_match{keys = {'addr:housename'}}
-
-HOUSENUMBER_TAGS = tag_match{keys = {'addr:housenumber', 'addr:conscriptionnumber',
-                                     'addr:streetnumber'}}
-
-INTERPOLATION_TAGS = tag_match{keys = {'addr:interpolation'}}
+                                 'addr:country', 'is_in:country'},
+                      interpolation = {'addr:interpolation'}
+                     }
 
-ADDRESS_TAGS = tag_match{keys = {'addr:*', 'is_in:*', 'tiger:county'}}
 
-SAVE_EXTRA_MAINS = true
+flex.set_unused_handling{delete_keys = {'tiger:*'}}
 
+return flex
diff --git a/settings/import-extratags.style b/settings/import-extratags.style
deleted file mode 100644 (file)
index 76146de..0000000
+++ /dev/null
@@ -1,237 +0,0 @@
-[
-{
-    "keys" : ["*source"],
-    "values" : {
-        "" : "skip"
-    }
-},
-{
-    "keys" : ["*:prefix", "*:suffix", "name:prefix:*", "name:suffix:*",
-              "name:etymology", "name:signed", "name:botanical", "wikidata", "*:wikidata",
-              "addr:street:name", "addr:street:type"],
-    "values" : {
-        "" : "extra"
-    }
-},
-{
-    "keys" : ["ref", "int_ref", "nat_ref", "reg_ref", "loc_ref", "old_ref",
-              "iata", "icao", "pcode", "pcode:*", "ISO3166-2"],
-    "values" : {
-        "" : "ref"
-    }
-},
-{
-    "keys" : ["name", "name:*", "int_name", "int_name:*", "nat_name", "nat_name:*",
-              "reg_name", "reg_name:*", "loc_name", "loc_name:*",
-              "old_name", "old_name:*", "alt_name", "alt_name:*", "alt_name_*",
-              "official_name", "official_name:*", "place_name", "place_name:*",
-              "short_name", "short_name:*", "brand"],
-    "values" : {
-        "" : "name"
-    }
-},
-{
-    "keys" : ["addr:housename"],
-    "values" : {
-        "" : "name,house"
-    }
-},
-{
-    "keys" : ["emergency"],
-    "values" : {
-        "fire_hydrant" : "skip",
-        "yes" : "skip",
-        "no" : "skip",
-        "" : "main"
-    }
-},
-{
-    "keys" : ["historic", "military"],
-    "values" : {
-        "no" : "skip",
-        "yes" : "skip",
-        "" : "main"
-    }
-},
-{
-    "keys" : ["natural"],
-    "values" : {
-        "yes" : "skip",
-        "no" : "skip",
-        "coastline" : "skip",
-        "" : "main,with_name"
-    }
-},
-{
-    "keys" : ["landuse"],
-    "values" : {
-        "cemetry" : "main,with_name",
-        "" : "main,fallback,with_name"
-    }
-},
-{
-    "keys" : ["highway"],
-    "values" : {
-        "no" : "skip",
-        "turning_circle" : "skip",
-        "mini_roundabout" : "skip",
-        "noexit" : "skip",
-        "crossing" : "skip",
-        "give_way" : "skip",
-        "stop" : "skip",
-        "street_lamp" : "main,with_name",
-        "traffic_signals" : "main,with_name",
-        "service" : "main,with_name",
-        "cycleway" : "main,with_name",
-        "path" : "main,with_name",
-        "footway" : "main,with_name",
-        "steps" : "main,with_name",
-        "bridleway" : "main,with_name",
-        "track" : "main,with_name",
-        "byway": "main,with_name",
-        "motorway_link" : "main,with_name",
-        "trunk_link" : "main,with_name",
-        "primary_link" : "main,with_name",
-        "secondary_link" : "main,with_name",
-        "tertiary_link" : "main,with_name",
-        "" : "main"
-    }
-},
-{
-    "keys" : ["railway"],
-    "values" : {
-        "level_crossing" : "skip",
-        "no" : "skip",
-        "rail" : "extra",
-        "" : "main,with_name"
-    }
-},
-{
-    "keys" : ["man_made"],
-    "values" : {
-        "survey_point" : "skip",
-        "cutline" : "skip",
-        "" : "main"
-    }
-},
-{
-    "keys" : ["aerialway"],
-    "values" : {
-        "pylon" : "skip",
-        "no" : "skip",
-        "" : "main"
-    }
-},
-{
-    "keys" : ["boundary"],
-    "values" : {
-        "place" : "skip",
-        "postal_code" : "main",
-        "" : "main,with_name"
-    }
-},
-{
-    "keys" : ["aeroway", "amenity", "club", "craft", "leisure",
-              "office", "mountain_pass"],
-    "values" : {
-        "no" : "skip",
-        "" : "main"
-    }
-},
-{
-    "keys" : ["shop"],
-    "values" : {
-        "no" : "skip",
-        "" : "main"
-    }
-},
-{
-    "keys" : ["tourism"],
-    "values" : {
-        "yes" : "skip",
-        "no" : "skip",
-        "" : "main"
-    }
-},
-{
-    "keys" : ["bridge", "tunnel"],
-    "values" : {
-        "" : "main,with_name_key"
-    }
-},
-{
-    "keys" : ["waterway"],
-    "values" : {
-        "riverbank" : "skip",
-        "" : "main,with_name"
-    }
-},
-{
-    "keys" : ["place"],
-    "values" : {
-        "" : "main"
-    }
-},
-{
-    "keys" : ["junction", "healthcare"],
-    "values" : {
-        "" : "main,fallback,with_name"
-    }
-},
-{
-    "keys" : ["postal_code", "postcode", "addr:postcode",
-              "tiger:zip_left", "tiger:zip_right"],
-    "values" : {
-        "" : "postcode,fallback"
-    }
-},
-{
-    "keys" : ["country_code", "ISO3166-1", "is_in:country_code", "is_in:country",
-              "addr:country", "addr:country_code"],
-    "values" : {
-        "" : "country"
-    }
-},
-{
-    "keys" : ["addr:housenumber", "addr:conscriptionnumber", "addr:streetnumber"],
-    "values" : {
-        "" : "address,house"
-    }
-},
-{
-    "keys" : ["addr:interpolation"],
-    "values" : {
-        "" : "interpolation,address"
-    }
-},
-{
-    "keys" : ["addr:*", "is_in:*", "tiger:county"],
-    "values" : {
-        "" : "address"
-    }
-},
-{
-    "keys" : ["building"],
-    "values" : {
-        "no" : "skip",
-        "" : "main,fallback,with_name"
-    }
-},
-{
-    "keys" : ["note", "note:*", "source", "source*", "attribution",
-              "comment", "fixme", "FIXME", "created_by", "tiger:*", "NHD:*",
-              "nhd:*", "gnis:*", "geobase:*", "KSJ2:*", "yh:*",
-              "osak:*", "naptan:*", "CLC:*", "import", "it:fvg:*",
-              "type", "lacounty:*", "ref:ruian:*", "building:ruian:type",
-              "ref:linz:*"],
-    "values" : {
-        "" : "skip"
-    }
-},
-{
-    "keys" : [""],
-    "values" : {
-        "" : "extra"
-    }
-}
-]
diff --git a/settings/import-full.lua b/settings/import-full.lua
new file mode 100644 (file)
index 0000000..a932fa5
--- /dev/null
@@ -0,0 +1,114 @@
+local flex = require('flex-base')
+
+flex.set_main_tags{
+    building = 'fallback',
+    emergency = 'always',
+    healthcare = 'fallback',
+    historic = 'always',
+    military = 'always',
+    natural = 'named',
+    landuse = 'named',
+    highway = {'always',
+               street_lamp = 'named',
+               traffic_signals = 'named',
+               service = 'named',
+               cycleway = 'named',
+               path = 'named',
+               footway = 'named',
+               steps = 'named',
+               bridleway = 'named',
+               track = 'named',
+               motorway_link = 'named',
+               trunk_link = 'named',
+               primary_link = 'named',
+               secondary_link = 'named',
+               tertiary_link = 'named'},
+    railway = 'named',
+    man_made = 'always',
+    aerialway = 'always',
+    boundary = {'named',
+                postal_code = 'named'},
+    aeroway = 'always',
+    amenity = 'always',
+    club = 'always',
+    craft = 'always',
+    junction = 'fallback',
+    landuse = 'fallback',
+    leisure = 'always',
+    office = 'always',
+    mountain_pass = 'always',
+    shop = 'always',
+    tourism = 'always',
+    bridge = 'named_with_key',
+    tunnel = 'named_with_key',
+    waterway = 'named',
+    place = 'always'
+}
+
+flex.set_prefilters{delete_keys = {'note', 'note:*', 'source', '*source', 'attribution',
+                                   'comment', 'fixme', 'FIXME', 'created_by', 'NHD:*',
+                                   'nhd:*', 'gnis:*', 'geobase:*', 'KSJ2:*', 'yh:*',
+                                   'osak:*', 'naptan:*', 'CLC:*', 'import', 'it:fvg:*',
+                                   'type', 'lacounty:*', 'ref:ruian:*', 'building:ruian:type',
+                                   'ref:linz:*', 'is_in:postcode'},
+                    delete_tags = {emergency = {'yes', 'no', 'fire_hydrant'},
+                                   historic = {'yes', 'no'},
+                                   military = {'yes', 'no'},
+                                   natural = {'yes', 'no', 'coastline'},
+                                   highway = {'no', 'turning_circle', 'mini_roundabout',
+                                              'noexit', 'crossing', 'give_way', 'stop'},
+                                   railway = {'level_crossing', 'no', 'rail'},
+                                   man_made = {'survey_point', 'cutline'},
+                                   aerialway = {'pylon', 'no'},
+                                   aeroway = {'no'},
+                                   amenity = {'no'},
+                                   club = {'no'},
+                                   craft = {'no'},
+                                   leisure = {'no'},
+                                   office = {'no'},
+                                   mountain_pass = {'no'},
+                                   shop = {'no'},
+                                   tourism = {'yes', 'no'},
+                                   bridge = {'no'},
+                                   tunnel = {'no'},
+                                   waterway = {'riverbank'},
+                                   building = {'no'},
+                                   boundary = {'place'}},
+                    extra_keys = {'*:prefix', '*:suffix', 'name:prefix:*', 'name:suffix:*',
+                               'name:etymology', 'name:signed', 'name:botanical',
+                               'wikidata', '*:wikidata',
+                               'addr:street:name', 'addr:street:type'}
+                   }
+
+flex.set_name_tags{main = {'name', 'name:*',
+                          'int_name', 'int_name:*',
+                          'nat_name', 'nat_name:*',
+                          'reg_name', 'reg_name:*',
+                          'loc_name', 'loc_name:*',
+                          'old_name', 'old_name:*',
+                          'alt_name', 'alt_name:*', 'alt_name_*',
+                          'official_name', 'official_name:*',
+                          'place_name', 'place_name:*',
+                          'short_name', 'short_name:*', 'brand'},
+                   extra = {'ref', 'int_ref', 'nat_ref', 'reg_ref',
+                            'loc_ref', 'old_ref',
+                            'iata', 'icao', 'pcode', 'pcode:*', 'ISO3166-2'},
+                   house = {'addr:housename'}
+                  }
+
+flex.set_address_tags{main = {'addr:housenumber',
+                              'addr:conscriptionnumber',
+                              'addr:streetnumber'},
+                      extra = {'addr:*', 'is_in:*', 'tiger:county'},
+                      postcode = {'postal_code', 'postcode', 'addr:postcode',
+                                  'tiger:zip_left', 'tiger:zip_right'},
+                      country = {'country_code', 'ISO3166-1',
+                                 'addr:country_code', 'is_in:country_code',
+                                 'addr:country', 'is_in:country'},
+                      interpolation = {'addr:interpolation'}
+                     }
+
+
+flex.set_unused_handling{extra_keys = {'place'}}
+
+return flex
diff --git a/settings/import-full.style b/settings/import-full.style
deleted file mode 100644 (file)
index ed874a1..0000000
+++ /dev/null
@@ -1,240 +0,0 @@
-[
-{
-    "keys" : ["*source"],
-    "values" : {
-        "" : "skip"
-    }
-},
-{
-    "keys" : ["*:prefix", "*:suffix", "name:prefix:*", "name:suffix:*",
-              "name:etymology", "name:signed", "name:botanical", "wikidata", "*:wikidata",
-              "addr:street:name", "addr:street:type"],
-    "values" : {
-        "" : "extra"
-    }
-},
-{
-    "keys" : ["ref", "int_ref", "nat_ref", "reg_ref", "loc_ref", "old_ref",
-              "iata", "icao", "pcode", "pcode:*", "ISO3166-2"],
-    "values" : {
-        "" : "ref"
-    }
-},
-{
-    "keys" : ["name", "name:*", "int_name", "int_name:*", "nat_name", "nat_name:*",
-              "reg_name", "reg_name:*", "loc_name", "loc_name:*",
-              "old_name", "old_name:*", "alt_name", "alt_name:*", "alt_name_*",
-              "official_name", "official_name:*", "place_name", "place_name:*",
-              "short_name", "short_name:*", "brand"],
-    "values" : {
-        "" : "name"
-    }
-},
-{
-    "keys" : ["addr:housename"],
-    "values" : {
-        "" : "name,house"
-    }
-},
-{
-    "keys" : ["emergency"],
-    "values" : {
-        "fire_hydrant" : "skip",
-        "yes" : "skip",
-        "no" : "skip",
-        "" : "main"
-    }
-},
-{
-    "keys" : ["historic", "military"],
-    "values" : {
-        "no" : "skip",
-        "yes" : "skip",
-        "" : "main"
-    }
-},
-{
-    "keys" : ["natural"],
-    "values" : {
-        "yes" : "skip",
-        "no" : "skip",
-        "coastline" : "skip",
-        "" : "main,with_name"
-    }
-},
-{
-    "keys" : ["landuse"],
-    "values" : {
-        "cemetry" : "main,with_name",
-        "" : "main,fallback,with_name"
-    }
-},
-{
-    "keys" : ["highway"],
-    "values" : {
-        "no" : "skip",
-        "turning_circle" : "skip",
-        "mini_roundabout" : "skip",
-        "noexit" : "skip",
-        "crossing" : "skip",
-        "give_way" : "skip",
-        "stop" : "skip",
-        "street_lamp" : "main,with_name",
-        "traffic_signals" : "main,with_name",
-        "service" : "main,with_name",
-        "cycleway" : "main,with_name",
-        "path" : "main,with_name",
-        "footway" : "main,with_name",
-        "steps" : "main,with_name",
-        "bridleway" : "main,with_name",
-        "track" : "main,with_name",
-        "byway": "main,with_name",
-        "motorway_link" : "main,with_name",
-        "trunk_link" : "main,with_name",
-        "primary_link" : "main,with_name",
-        "secondary_link" : "main,with_name",
-        "tertiary_link" : "main,with_name",
-        "" : "main"
-    }
-},
-{
-    "keys" : ["railway"],
-    "values" : {
-        "level_crossing" : "skip",
-        "no" : "skip",
-        "rail" : "skip",
-        "" : "main,with_name"
-    }
-},
-{
-    "keys" : ["man_made"],
-    "values" : {
-        "survey_point" : "skip",
-        "cutline" : "skip",
-        "" : "main"
-    }
-},
-{
-    "keys" : ["aerialway"],
-    "values" : {
-        "pylon" : "skip",
-        "no" : "skip",
-        "" : "main"
-    }
-},
-{
-    "keys" : ["boundary"],
-    "values" : {
-        "place" : "skip",
-        "postal_code" : "main",
-        "" : "main,with_name"
-    }
-},
-{
-    "keys" : ["aeroway", "amenity", "club", "craft", "leisure",
-              "office", "mountain_pass"],
-    "values" : {
-        "no" : "skip",
-        "" : "main"
-    }
-},
-{
-    "keys" : ["shop"],
-    "values" : {
-        "no" : "skip",
-        "" : "main"
-    }
-},
-{
-    "keys" : ["tourism"],
-    "values" : {
-        "yes" : "skip",
-        "no" : "skip",
-        "" : "main"
-    }
-},
-{
-    "keys" : ["bridge", "tunnel"],
-    "values" : {
-        "" : "main,with_name_key"
-    }
-},
-{
-    "keys" : ["waterway"],
-    "values" : {
-        "riverbank" : "skip",
-        "" : "main,with_name"
-    }
-},
-{
-    "keys" : ["place"],
-    "values" : {
-        "" : "main"
-    }
-},
-{
-    "keys" : ["junction", "healthcare"],
-    "values" : {
-        "" : "main,fallback,with_name"
-    }
-},
-{
-    "keys" : ["postal_code", "postcode", "addr:postcode",
-              "tiger:zip_left", "tiger:zip_right"],
-    "values" : {
-        "" : "postcode,fallback"
-    }
-},
-{
-    "keys" : ["country_code", "ISO3166-1", "is_in:country_code", "is_in:country",
-              "addr:country", "addr:country_code"],
-    "values" : {
-        "" : "country"
-    }
-},
-{
-    "keys" : ["addr:housenumber", "addr:conscriptionnumber", "addr:streetnumber"],
-    "values" : {
-        "" : "address,house"
-    }
-},
-{
-    "keys" : ["addr:interpolation"],
-    "values" : {
-        "" : "interpolation,address"
-    }
-},
-{
-    "keys" : ["addr:*", "is_in:*", "tiger:county"],
-    "values" : {
-        "" : "address"
-    }
-},
-{
-    "keys" : ["building"],
-    "values" : {
-        "no" : "skip",
-        "" : "main,fallback,with_name"
-    }
-},
-{
-    "keys" : ["tracktype", "traffic_calming", "service", "cuisine", "capital",
-              "dispensing", "religion", "denomination", "sport",
-              "internet_access", "lanes", "surface", "smoothness", "width",
-              "est_width", "incline", "opening_hours", "collection_times",
-              "service_times", "disused", "wheelchair", "sac_scale",
-              "trail_visibility", "mtb:scale", "mtb:description", "wood",
-              "drive_through", "drive_in", "access", "vehicle", "bicyle",
-              "foot", "goods", "hgv", "motor_vehicle", "motor_car", "oneway",
-              "date_on", "date_off", "day_on", "day_off", "hour_on", "hour_off",
-              "maxweight", "maxheight", "maxspeed", "fee", "toll", "charge",
-              "population", "description", "image", "attribution", "fax",
-              "email", "url", "website", "phone", "real_ale", "smoking",
-              "food", "camera", "brewery", "locality", "wikipedia",
-              "wikipedia:*", "access:*", "contact:*", "drink:*", "toll:*",
-              "area"],
-    "values" : {
-        "" : "extra"
-    }
-}
-]
diff --git a/settings/import-street.lua b/settings/import-street.lua
new file mode 100644 (file)
index 0000000..acadf01
--- /dev/null
@@ -0,0 +1,68 @@
+local flex = require('flex-base')
+
+flex.set_main_tags{
+    highway = {'always',
+               street_lamp = 'named',
+               traffic_signals = 'named',
+               service = 'named',
+               cycleway = 'named',
+               path = 'named',
+               footway = 'named',
+               steps = 'named',
+               bridleway = 'named',
+               track = 'named',
+               motorway_link = 'named',
+               trunk_link = 'named',
+               primary_link = 'named',
+               secondary_link = 'named',
+               tertiary_link = 'named'},
+    boundary = {administrative = 'named'},
+    landuse = 'fallback',
+    place = 'always'
+}
+
+flex.set_prefilters{delete_keys = {'building', 'source',
+                                   'addr:housenumber', 'addr:street',
+                                   'source', '*source', 'type',
+                                   'is_in:postcode', '*:wikidata',
+                                   '*:prefix', '*:suffix', 'name:prefix:*', 'name:suffix:*',
+                                   'name:etymology', 'name:signed', 'name:botanical',
+                                   'addr:street:name', 'addr:street:type'},
+                    delete_tags = {highway = {'no', 'turning_circle', 'mini_roundabout',
+                                              'noexit', 'crossing', 'give_way', 'stop'},
+                                   landuse = {'cemetry', 'no'},
+                                   boundary = {'place'}},
+                    extra_keys = {'wikipedia', 'wikipedia:*', 'wikidata', 'capital', 'area'}
+                   }
+
+flex.set_name_tags{main = {'name', 'name:*',
+                          'int_name', 'int_name:*',
+                          'nat_name', 'nat_name:*',
+                          'reg_name', 'reg_name:*',
+                          'loc_name', 'loc_name:*',
+                          'old_name', 'old_name:*',
+                          'alt_name', 'alt_name:*', 'alt_name_*',
+                          'official_name', 'official_name:*',
+                          'place_name', 'place_name:*',
+                          'short_name', 'short_name:*', 'brand'},
+                   extra = {'ref', 'int_ref', 'nat_ref', 'reg_ref',
+                            'loc_ref', 'old_ref',
+                            'iata', 'icao', 'pcode', 'pcode:*', 'ISO3166-2'}
+                  }
+
+flex.set_address_tags{main = {'addr:housenumber',
+                              'addr:conscriptionnumber',
+                              'addr:streetnumber'},
+                      extra = {'addr:*', 'is_in:*', 'tiger:county'},
+                      postcode = {'postal_code', 'postcode', 'addr:postcode',
+                                  'tiger:zip_left', 'tiger:zip_right'},
+                      country = {'country_code', 'ISO3166-1',
+                                 'addr:country_code', 'is_in:country_code',
+                                 'addr:country', 'is_in:country'},
+                      interpolation = {'addr:interpolation'},
+                      postcode_fallback = false
+                     }
+
+flex.set_unused_handling{extra_keys = {'place'}}
+
+return flex
diff --git a/settings/import-street.style b/settings/import-street.style
deleted file mode 100644 (file)
index a1b0e8d..0000000
+++ /dev/null
@@ -1,92 +0,0 @@
-[
-{   "keys" : ["wikipedia", "wikipedia:*", "wikidata", "area"],
-    "values" : {
-        "" : "extra"
-    }
-},
-{
-    "keys" : ["*:prefix", "*:suffix", "name:prefix:*", "name:suffix:*",
-              "name:etymology", "name:signed", "name:botanical", "*:wikidata",
-              "addr:street:name", "addr:street:type"],
-    "values" : {
-        "" : "skip"
-    }
-},
-{
-    "keys" : ["ref", "int_ref", "nat_ref", "reg_ref", "loc_ref", "old_ref",
-              "iata", "icao", "pcode", "ISO3166-2"],
-    "values" : {
-        "" : "ref"
-    }
-},
-{
-    "keys" : ["name", "name:*", "int_name", "int_name:*", "nat_name", "nat_name:*",
-              "reg_name", "reg_name:*", "loc_name", "loc_name:*",
-              "old_name", "old_name:*", "alt_name", "alt_name:*", "alt_name_*",
-              "official_name", "official_name:*", "place_name", "place_name:*",
-              "short_name", "short_name:*", "brand"],
-    "values" : {
-        "" : "name"
-    }
-},
-{
-    "keys" : ["landuse"],
-    "values" : {
-        "cemetry" : "skip",
-        "" : "fallback,with_name"
-    }
-},
-{
-    "keys" : ["boundary"],
-    "values" : {
-        "administrative" : "main"
-    }
-},
-{
-    "keys" : ["place"],
-    "values" : {
-        "" : "main"
-    }
-},
-{
-    "keys" : ["country_code", "ISO3166-1", "is_in:country_code", "is_in:country",
-              "addr:country", "addr:country_code"],
-    "values" : {
-        "" : "country"
-    }
-},
-{
-    "keys" : ["addr:*", "is_in:*", "tiger:county"],
-    "values" : {
-        "" : "address"
-    }
-},
-{
-    "keys" : ["highway"],
-    "values" : {
-        "motorway" : "main",
-        "trunk" : "main",
-        "primary" : "main",
-        "secondary" : "main",
-        "tertiary" : "main",
-        "unclassified" : "main",
-        "residential" : "main",
-        "living_street" : "main",
-        "pedestrian" : "main",
-        "road" : "main",
-        "service" : "main,with_name",
-        "cycleway" : "main,with_name",
-        "path" : "main,with_name",
-        "footway" : "main,with_name",
-        "steps" : "main,with_name",
-        "bridleway" : "main,with_name",
-        "track" : "main,with_name",
-        "byway": "main,with_name",
-        "motorway_link" : "main,with_name",
-        "trunk_link" : "main,with_name",
-        "primary_link" : "main,with_name",
-        "secondary_link" : "main,with_name",
-        "tertiary_link" : "main,with_name"
-    }
-}
-]
diff --git a/test/bdd/osm2pgsql/import/custom_style.feature b/test/bdd/osm2pgsql/import/custom_style.feature
new file mode 100644 (file)
index 0000000..2ca95c9
--- /dev/null
@@ -0,0 +1,200 @@
+@DB
+Feature: Import with custom styles by osm2pgsql
+    Tests for the example customizations given in the documentation.
+
+    Scenario: Custom main tags
+        Given the lua style file
+            """
+            local flex = require('import-full')
+
+            flex.set_main_tags{
+                boundary = {administrative = 'named'},
+                highway = {'always', street_lamp = 'named'},
+                landuse = 'fallback'
+            }
+            """
+        When loading osm data
+            """
+            n10 Tboundary=administrative x0 y0
+            n11 Tboundary=administrative,name=Foo x0 y0
+            n12 Tboundary=electoral x0 y0
+            n13 Thighway=primary x0 y0
+            n14 Thighway=street_lamp x0 y0
+            n15 Thighway=primary,landuse=street x0 y0
+            """
+        Then place contains exactly
+            | object | class    | type           |
+            | N11    | boundary | administrative |
+            | N13    | highway  | primary        |
+            | N15    | highway  | primary        |
+
+    Scenario: Prefiltering tags
+        Given the lua style file
+            """
+            local flex = require('import-full')
+
+            flex.set_prefilters{
+                delete_keys = {'source', 'source:*'},
+                extra_tags = {amenity = {'yes', 'no'}}
+            }
+            flex.set_main_tags{
+                amenity = 'always',
+                tourism = 'always'
+            }
+            """
+        When loading osm data
+            """
+            n1 Tamenity=yes x0 y6
+            n2 Tamenity=hospital,source=survey x3 y6
+            n3 Ttourism=hotel,amenity=yes x0 y0
+            n4 Ttourism=hotel,amenity=telephone x0 y0
+            """
+        Then place contains exactly
+            | object     | extratags              |
+            | N2:amenity | -                      |
+            | N3:tourism | 'amenity': 'yes'       |
+            | N4:tourism | - |
+            | N4:amenity | - |
+
+    Scenario: Name tags
+        Given the lua style file
+            """
+            local flex = require('flex-base')
+
+            flex.set_main_tags{highway = {traffic_light = 'named'}}
+            flex.set_name_tags{main = {'name', 'name:*'},
+                               extra = {'ref'}
+                              }
+            """
+        When loading osm data
+            """
+            n1 Thighway=stop,name=Something x0 y0
+            n2 Thighway=traffic_light,ref=453-4 x0 y0
+            n3 Thighway=traffic_light,name=Greens x0 y0
+            n4 Thighway=traffic_light,name=Red,ref=45 x0 y0
+            """
+        Then place contains exactly
+            | object     | name                       |
+            | N3:highway | 'name': 'Greens'           |
+            | N4:highway | 'name': 'Red', 'ref': '45' |
+
+    Scenario: Address tags
+        Given the lua style file
+            """
+            local flex = require('import-full')
+
+            flex.set_address_tags{
+                main = {'addr:housenumber'},
+                extra = {'addr:*'},
+                postcode = {'postal_code', 'postcode', 'addr:postcode'},
+                country = {'country-code', 'ISO3166-1'}
+            }
+            """
+        When loading osm data
+            """
+            n1 Ttourism=hotel,addr:street=Foo x0 y0
+            n2 Taddr:housenumber=23,addr:street=Budd,postal_code=5567 x0 y0
+            n3 Taddr:street=None,addr:city=Where x0 y0
+            """
+        Then place contains exactly
+            | object     | type  | address |
+            | N1:tourism | hotel | 'street': 'Foo' |
+            | N2:place   | house | 'housenumber': '23', 'street': 'Budd', 'postcode': '5567' |
+
+    Scenario: Unused handling
+        Given the lua style file
+            """
+            local flex = require('import-full')
+
+            flex.set_address_tags{
+                main = {'addr:housenumber'},
+                extra = {'addr:*', 'tiger:county'}
+            }
+            flex.set_unused_handling{delete_keys = {'tiger:*'}}
+            """
+        When loading osm data
+            """
+            n1 Ttourism=hotel,tiger:county=Fargo x0 y0
+            n2 Ttourism=hotel,tiger:xxd=56,else=other x0 y0
+            """
+        Then place contains exactly
+            | object     | type  | address                 | extratags        |
+            | N1:tourism | hotel | 'tiger:county': 'Fargo' | -                |
+            | N2:tourism | hotel | -                       | 'else': 'other'  |
+
+    Scenario: Additional relation types
+        Given the lua style file
+            """
+            local flex = require('import-full')
+
+            flex.RELATION_TYPES['site'] = flex.relation_as_multipolygon
+            """
+        And the grid
+            | 1 | 2 |
+            | 4 | 3 |
+        When loading osm data
+            """
+            n1
+            n2
+            n3
+            n4
+            w1 Nn1,n2,n3,n4,n1
+            r1 Ttype=multipolygon,amenity=school Mw1@
+            r2 Ttype=site,amenity=school Mw1@
+            """
+        Then place contains exactly
+            | object     | type   |
+            | R1:amenity | school |
+            | R2:amenity | school |
+
+    Scenario: Exclude country relations
+        Given the lua style file
+            """
+            local flex = require('import-full')
+
+            function osm2pgsql.process_relation(object)
+                if object.tags.boundary ~= 'administrative' or object.tags.admin_level ~= '2' then
+                  flex.process_relation(object)
+                end
+            end
+            """
+       And the grid
+            | 1 | 2 |
+            | 4 | 3 |
+       When loading osm data
+            """
+            n1
+            n2
+            n3
+            n4
+            w1 Nn1,n2,n3,n4,n1
+            r1 Ttype=multipolygon,boundary=administrative,admin_level=4,name=Small Mw1@
+            r2 Ttype=multipolygon,boundary=administrative,admin_level=2,name=Big Mw1@
+            """
+        Then place contains exactly
+            | object      | type           |
+            | R1:boundary | administrative |
+
+    Scenario: Customize processing functions
+        Given the lua style file
+            """
+            local flex = require('import-full')
+
+            local original_process_tags = flex.process_tags
+
+            function flex.process_tags(o)
+                if o.object.tags.highway ~= nil and o.object.tags.access == 'no' then
+                    return
+                end
+
+                original_process_tags(o)
+            end
+            """
+        When loading osm data
+            """
+            n1 Thighway=residential x0 y0
+            n2 Thighway=residential,access=no x0 y0
+            """
+        Then place contains exactly
+            | object     | type        |
+            | N1:highway | residential |
index 60d241fe68e9d3296e82ae9c0938882d7b991eab..c2ab736aa38859cb51ee3fa6e711c71defa2c0b8 100644 (file)
@@ -122,10 +122,10 @@ Feature: Tag evaluation
             n8003 Tshop=shoes,name:source=survey
             """
         Then place contains exactly
-            | object | class | extratags    |
-            | N8001  | shop  | 'xx': 'yy'   |
-            | N8002  | shop  | 'ele': '234' |
-            | N8003  | shop  | -            |
+            | object | class | name | extratags    |
+            | N8001  | shop  |  -   | 'xx': 'yy'   |
+            | N8002  | shop  |  -   | 'ele': '234' |
+            | N8003  | shop  |  -   | -            |
 
 
     Scenario: Admin levels
index 238081c0c87a4b23aa03693aa6244fac310802ac..6179ca3404030e593552958356f85f65a950982c 100644 (file)
@@ -89,11 +89,6 @@ class NominatimEnvironment:
         if self.db_pass:
             dsn += ';password=' + self.db_pass
 
-        if self.website_dir is not None \
-           and self.test_env is not None \
-           and dsn == self.test_env['NOMINATIM_DATABASE_DSN']:
-            return # environment already set uo
-
         self.test_env = dict(self.default_config)
         self.test_env['NOMINATIM_DATABASE_DSN'] = dsn
         self.test_env['NOMINATIM_LANGUAGES'] = 'en,de,fr,ja'
index 7590b17c9c81c7a97c4f9cfc0bdf339db2cc4405..336fb707038bc02d775f84c85ba3165f3b583221 100644 (file)
@@ -49,6 +49,15 @@ def write_opl_file(opl, grid):
 
         return fd.name
 
+@given('the lua style file')
+def lua_style_file(context):
+    """ Define a custom style file to use for the import.
+    """
+    style = Path(context.nominatim.website_dir.name) / 'custom.lua'
+    style.write_text(context.text)
+    context.nominatim.test_env['NOMINATIM_IMPORT_STYLE'] = str(style)
+
+
 @given(u'the ([0-9.]+ )?grid(?: with origin (?P<origin>.*))?')
 def define_node_grid(context, grid_step, origin):
     """
index a003065d8e87f11e8d3cf77e74028ca3b971a8fb..ff8b587da2e9f875625093380405aec64a0b6ed5 100644 (file)
@@ -222,7 +222,7 @@ def test_get_import_style_intern(make_config, src_dir, monkeypatch):
 
     monkeypatch.setenv('NOMINATIM_IMPORT_STYLE', 'street')
 
-    expected = src_dir / 'settings' / 'import-street.style'
+    expected = src_dir / 'settings' / 'import-street.lua'
 
     assert config.get_import_style_file() == expected