Introduction

osm-gimmisn is a web-based tool to find objects missing from the OpenStreetMap database. As a start, it finds missing streets and house numbers based on a reference list.

The latest version is v24.2, released on 2024-02-01. See the release notes.

It works by fetching the street and house number list for a relation (area), then suggesting what possibly missing objects are a good idea to survey.

You can see this in action at public instances: vmiklos.hu, vasony.hu.

Website

Check out the project's website for a list of features and installation and usage information.

Platforms

osm-gimmisn has been used on Linux, but the data validator is known to work Windows as well.

The important bits of the code

  • The entry point is the application() function in src/wsgi.rs.

  • The test code lives under src/*/test.rs.

  • The documentation is under guide/.

License

Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. See the license bundle for details.

Installation

HW requirements

  • at least 1 CPU core

  • at least 1 GB memory

  • at least 1 GB disk space

Install steps

  • Install dependencies, e.g. on Fedora:
dnf install cargo
dnf install clang-devel
dnf install git
dnf install libicu-devel
dnf install llvm-devel
dnf install make
dnf install npm
dnf install openssl-devel
dnf install sqlite-devel

If the npm version in your distribution is too old: sudo npm install -g n && sudo n stable.

  • Clone the repo:
git clone https://github.com/vmiklos/osm-gimmisn
cd osm-gimmisn
  • Build the code and cached data:
make

Populate the reference directory with TSV files for the house number and street list:

osm-gimmisn sync-ref --mode download --url https://osm.example.com/data/

Install steps (Windows)

  • Install git and cargo.

  • Open e.g. the Command Prompt and clone the repo (see above).

  • Build the code:

cargo build --release --no-default-features
  • Run the validator:
osm-gimmisn.exe validator data\relation-budapest_11.yaml

Developer setup

make run

This allows accessing your local instance for development.

Production setup

  • The launcher is osm-gimmisn rouille.

  • A sample osm-gimmisn.service is provided, you can copy that to /etc/systemd/system/osm-gimmisn.service and customize it to your needs.

  • Use systemctl start osm-gimmisn.service to run the app server.

  • Optionally, set up a reverse proxy with SSL support.

  • Optionally, add cron as a daily crontab:

# daily, at 0:05
5 0 * * * cd /home/osm-gimmisn/git/osm-gimmisn && target/release/osm-gimmisn cron --mode all

See osm-gimmisn cron --help for details on what switches are supported for that tool.

Custom configuration

workdir/wsgi.ini contains the configuration. Common keys to be customized (showing the defaults):

uri_prefix = '/osm'
tcp_port = '8000'
overpass_uri = 'https://z.overpass-api.de'
cron_update_inactive = 'False'

Running within a container

You can try osm-gimmisn in 5 minutes following these basic steps:

  1. Clone the repo:
git clone https://github.com/vmiklos/osm-gimmisn
  1. Build the container:
cd osm-gimmisn/tools/container && ./build.sh
  1. Run the container:
./run.sh
  1. Sync the reference data:
podman exec -t -i osm-gimmisn bash -c 'cd /opt/osm-gimmisn && target/release/osm-gimmisn sync-ref --mode download --url https://osm.example.com/data/'
  1. Go to http://0.0.0.0:8000/osm/ in your web browser.

Note that the comparison results are not only affected by changes in the OSM database, false positives are also silenced by osm-gimmisn filters. To update those filters, run:

podman exec -t -i osm-gimmisn bash -c 'cd /opt/osm-gimmisn && git pull -r && make data/yamls.cache'

from time to time.

Also note that you can decide what container manager you want to use. It's not a problem if you prefer docker instead of podman, the commands documented here are still meant to work.

Usage

Up to date list of missing streets and house numbers

The website provides you with hints regarding where to map house numbers. The main page has the following columns:

  • House number coverage: lists missing house numbers.

  • Existing house numbers for an area: this is from the OSM database.

  • Street coverage: lists missing streets.

  • Existing streets for an area: this is from the OSM database.

  • Area on the OSM website, you can see its boundary clearly there.

It is recommended to focus on the house number coverage, at least initially. If you see an interesting area there, then you can find hints regarding what to survey. Consider the case when the area is already in the list, the house number is indeed missing, and you just created an OSM change to add it. The website is automatically updated in this case on a daily basis. You can refresh manually if you want faster feedback:

  • Wait a few minutes. If you just edited OSM, then running an overpass query right now will likely work with outdated data.

  • Go to the missing house numbers section of your area, and click on the 'Update from OSM' link.

  • Once the query is complete, the updated content should no longer mention your contributed house number(s) as missing anymore.

The missing house numbers are colored:

  • black means a residential house number

  • blue means a commercial house number (text view: * suffix)

  • commercial house numbers can have comments, you can see them if you hover your mouse over them

NOTE: in case there is both a letter suffix and a source suffix, then the syntax is 42/A*, i.e. first the letter suffix, and then the source suffix.

How to add a new area

A settlement, village or district of a large city is represented in the OSM database as relations. Based on this, osm-gimmisn refers to managed areas as relations. To add a new relation, you need to do the following steps:

  • Be prepared to edit the git repository. This is possible via command-line git clone or via web-based editing.

  • Search for the relation on osm.org, e.g. 'Kelenföld, Budapest'. The first hit is usually a relation link, e.g. https://www.openstreetmap.org/relation/2700869. Now you know the OSM identifier of the relation.

You'll also need the county reference ('refcounty' below) and settlement reference ('refsettlement' below) of the area, you can find codes for Hungary in the refcodes file.

(These codes are specific to Hungary, but in case your country has a larger and smaller container for streets and house numbers, it's easy to adapt.)

  • Add a new entry to the data/relations.yaml file in the git repository, using the following form:
kelenfold:
    missing-streets: "no"
    osmrelation: 2700869
    refcounty: "01"
    refsettlement: "011"

Names are snake_case by convention, e.g. kelenfold. The other fields should match the values you obtained in the previous bullet point. (missing-streets: "no" means that this OSM relation is only a subset of the referenced settlement, so it's pointless to search for missing streets here.)

  • Finally you can send your modification as a pull request, it'll be probably accepted after review.

Filtering (out) incorrect information

This action is similar to adding a new relation, but in this case you'll need to work with a file dedicated to detailed information about the relation. The path derives from the relation name, e.g. relation-magasut.yaml. Consider the case when you browse the Magasút (area) house number coverage and you see a hint that the odd side of Magasúti köz (street) misses a house number: 999. Let's also assume that you did your survey and you know that there is no such house number to be added to the OSM database. The following steps area needed to silence this hint of osm-gimmisn:

  • Survey the Magasúti köz street and note down which odd and even house number ranges are present for the street. Usually there are tables at the corners showing this information.

  • Let's assume you found that the odd side is 1 to 9, the even side is 2 to 8. Now you just need to describe this, and osm-gimmisn will infer that 999 is noise in the reference data.

  • Edit the relation-magasut.yaml file. If there is no such file, then the easiest is to copy an existing one and delete all streets.

  • You can describe the Magasúti köz street like this:

  Magasúti köz:
    ranges:
      - {start: '1', end: '9'}
      - {start: '2', end: '8'}

This is a machine-readable way of describing your survey result. In case the OSM and the reference name of the street differs, use the OSM name here.

  • Send a pull request to contribute your created filter, and then the website will be updated accordingly.

In other words, there is only minimal filtering for the reference data (1-999 and 2-998 is accepted) by default. If you want to filter out noise, then you need to cover the correct house numbers with ranges, and whatever is not in this range will be filtered out.

An alternative way of filtering out invalid data from the reference is explicitly stating what items are invalid:

  Magasúti köz:
    invalid: ['7', '11']

Sometimes this results in a more compact filter than the above presented ranges way. Note that the values of the invalid list is compared to house numbers after normalization, e.g. '47/49D' can be filtered out with '47'.

The items of the invalid list are normalized, the same way as reference house numbers. So in case '42a' is normalized to '42', you can write '42a' in the invalid list, and it'll silence '42'. This has the benefit that the invalid items will keep working even if you later decide to set the housenumber-letters: true mode.

Searching for missing streets

The yaml format is like this:

  • missing-streets: only: this is provided in the root of the relation-NAME.yaml file (by convention). It denotes that you only want to search for streets in this (potentially large) relation. Other valid values are yes (which is the default) and no.

For each reported missing street, the outcomes can be the followings:

  • add the missing street to OSM, based on survey

  • add a mapping between the reference and OSM name if survey confirms that the name in OSM is correct (refstreets key)

  • silence the street name if it should have no equivalent in OSM (street-filters key)

Searching for additional streets

The purpose of this check is to detect street names in OSM, which are not in the reference, i.e. "additional" is the opposite of "missing".

For each reported additional street, the outcomes can be the followings:

  • fix the name of the additional street in OSM, based on survey

  • add a mapping between the reference and OSM name if survey confirms that the name in OSM is correct (refstreets key)

  • silence the street name if it should have no equivalent in the reference (osm-street-filters key)

Advanced topics

Apart from filtering out noise, you can also specify other settings, though these are needed less frequently:

  • refstreets: this key can be used in the root of a relation file, it's used to describe street name mappings, in case the OSM name and reference name differs and the OSM one is the correct name. The key is the OSM name and the value is the reference name. It's not valid to map multiple OSM names to the same reference name, so this has to be a 1:1 mapping. This makes it possible to map both ways using the same markup.

  • street-filters: this key can be used in the root of a relation file, it's used to silence false alarms during the 'missing streets' check when a reference street name should have no OSM street name equivalent.

  • osm-street-filters: this key can be used in the root of a relation file, it's used to silence false alarms during the 'additional streets' check when an OSM street name should have no reference street name equivalent.

  • refsettlement: this key can be used for a street. In case the majority of a relation has a given refsettlement value, but there are a few exceptions, then you can use this markup to override the relation-level value with a street-level one.

  • Range-level refsettlement: this is useful in case the two sides of a street has different refsettlement values (that side of the street belongs to a different district or settlement).

  • interpolation: this key can be specified for a street. Its all value means that the street has continuous numbering instead of even and odd sides.

  • show-refstreet: false: this key can be specified for a street. It means that in case the OSM and reference names would not match, don't show the reference name on the missing housenumbers -> view results page.

NOTE: This has a second effect as well. The /missing-streets/.../view-turbo page lists all OSM street names which have a mapping to reference names. Before presenting that list, items with this show-refstreet: false property are filtered out from the result. This supports a workflow where the mapping has guesses as well, and then survey clarifies those questionable items, so that either OSM is fixed or show-refstreet: false is added.

  • inactive: true: this key can be used for a relation, it disables the daily update (which would be a waste if e.g. the relation already has 100% coverage.) Manual updates are still possible.

  • You can download a GPX file showing the streets of the missing house numbers if you follow the 'Overpass turbo query for the below streets' link on the missing housenumbers page. To do this, visit the 'Overpass turbo' site from the toolbar, copy the query, run it, choose Export -> Download as GPX, and e.g. load the result into OsmAnd on your phone.

  • housenumber-letters: true: this key can be used to do micro-mapping, i.e. detect that e.g. 42/B is missing, even if 42/A is already mapped. Works with 42/2 and 42/1 as well. (The default behavior is to ignore any noise after the numeric value of the house numbers.)

  • alias: ["foo", "bar"]: this key can be used on relations to specify old names. This way bookmarks keep working, even in case a relation is renamed.

  • additional-housenumbers: true: this key can be used to opt-in to see house numbers which are on OSM but not in the reference. It's disabled by default as it may lead to unwanted vandalism. See below for details.

It is expected that "normalization" not only filters out noise from the reference, but also expands housenumber ranges in a sensible way. Here are some examples:

Case IDGiven a rangeWhen this setting is usedExpands to
1139range is {start: '137', end: '165'}139 as it is in range
2999range is {start: '137', end: '165'}Empty list as it is not in range
3xDefaultsEmpty list as it is not a number
41Defaults1, as the default ranges are 1-999 and 2-998
51;2Defaults1 and 2 as a semicolon is a separator
62-6Defaults2, 4, and 6 as the even range is expanded
75-8Defaults5 and 8 as the parity doesn't match
82-5interpolation=all2, 3, 4 and 5
9163-167range is {start: '137', end: '165'}163 and 165, no 167
102-2000Defaults2 because 2000 large(r than 1000)
112-56Defaults2 and 56 because the diff of two is large(r than 24)
120-42Defaults42 because 0 is too small
1342-1Defaults42 because -1 is considered as a suffix

See the tests in src/areas/tests.rs for even more details.

Additional house numbers analysis

This is the opposite of missing housenumbers, i.e. check for OSM objects which are not in the reference. This can be helpful to find errors, but use it with care: just because you did not find a housenumber by survey, it doesn't mean it has to be deleted.

The way to filter out valid data from the OSM list is to explicitly state what items are valid:

  Magasúti köz:
    valid: ['13', '15']

Automerge workflow for committers

If you contribute to osm-gimmisn frequently, then you'll likely get self-review permissions granted. Once that's the case you can use this workflow to submit your changes in a fire & forget way, from command-line:

  • Once: set up your ssh key

  • Once: git clone git@github.com:vmiklos/osm-gimmisn

  • Once: cd osm-gimmisn

  • For each PR: this can be repeated in case you want multiple commits in a single PR or CI finds an error:

git fetch --prune # if the PR has been merged on the server, then the remote private/$USER/master has been deleted, we learn about that here
git rebase origin/master # we work on a fresh master
... hack hack hack ...
git commit -a -m "data: blabla"
git show # optional but recommended: review your changes, "q" quits from less
git push origin master:private/$USER/master

Developer API

In case the /missing-housenumbers/.../view-result HTML output looks interesting to you and you would like to use that information in your application, no need to scrape the webpage, you can get the raw input of that analysis as /missing-housenumbers/.../view-result.json instead.

Similarly, the /additional-housenumbers/.../view-result HTML output has a matching /additional-housenumbers/.../view-result.json.

Development notes

Even tough Rust comes with its own cargo build tool, this project uses some build-time created caches, so always run make after changing the code and before using it.

Coding style

  • Build HTML strings using yattag, return a yattag::Doc if the result is an escaped string.

  • Path handling: make relative paths absolute using context::Context::get_abspath().

This has the benefit that real and test config/data (under / and tests/) can be separated via a single parameter to the context::Context constructor.

  • Try to keep module size under 1000 lines to avoid monster modules:
for i in $(git ls-files|grep rs$|grep -v tests.rs); do lines=$(wc -l < $i); if [ $lines -gt 1000 ]; then echo "$i is too large: $lines lines"; fi; done
  • Try to make JS optional. If a link can be handled with and without JS, then generate HTML which goes to the no-JS version, then JS can tweak the DOM to invoke JS instead.

TS debugging

Bundled JS can be minified (for production) and also source maps can be added (for debugging). The default output is for production, but touching a TS source file and invoking:

make TSDEBUG=1

produces output that is for debugging.

Rust debugging

make defaults to release builds. To switch to a debug build:

rm -rf target/
echo RSDEBUG=1 > config.mak
make

To run a single test:

RUST_BACKTRACE=1 cargo test --lib -- --exact --nocapture wsgi_json::tests::test_missing_streets_update_result_json

Tests follow the Favor real dependencies for unit testing pattern, i.e. apart from filesystem, network or time (see src/context.rs for the exact list), no mocking is used.

Debugging workdir/stats/stats.json generation:

cargo run -- cron --mode stats --no-overpass

Rust performance profiling

The symbols profile enables debug symbols while keeping optimizations on:

cargo build --profile symbols
valgrind --tool=callgrind target/symbols/osm-gimmisn missing_housenumbers budapest_11

YAML schema

The YAML schema is meant to provide reference documentation in the long run, so guide/src/usage.md can focus on tutorial documentation.

ajv validate -s data/relations.schema.yaml -d data/relations.yaml
for i in data/relation-*.yaml; do ajv validate -s data/relation.schema.yaml -d $i || break; done

Checklist

Ideally CI checks everything before a commit hits master, but here are a few things which are not part of CI:

Changelog

master

  • Resolves: gh#3744 cron: add a new --refarea switch
  • Resolves: gh#3558 , is now also recognized as a housenumber separator, similar to ;
  • Resolves: gh#3768 lints, invalid addr:city values: add new 'update from OSM' button

24.2

  • Resolves: gh#3073 New /missing-housenumbers/.../view-lints endpoint, listing per-relation lints (mostly flagging unused filters)
  • Resolves: gh#3288 additional streets, gpx: handle nodes and relations as well
  • Resolves: gh#3290 YAML keys are now flagged by the validator, instead of taking the last value
  • Resolves: gh#3456 /lints/whole-country/invalid-addr-cities is now case-sensitive, finds more problems
  • Resolves: gh#3105 update times in the footer now show both the OSM and areas timestamp instead of the time of the overpass query

7.6

  • Rouille: new --host parameter to specify the bind address
  • The /missing-housenumbers/.../update-result is now about 6 times faster (replaced home-grown JSON cache with SQL indexes)
  • New /lints/.../invalid-addr-cities endpoint, tries to find invalid addr:city values
  • Resolves: gh#2986 stats: the length of the invalid addr:city values list now has a chart
  • Resolves: gh#2987 stats: extract 2 lints from the stats page to an own lints page
  • Resolves: gh#2994 areas: find ref-not-in-reflist problems in Relation.get_invalid_refstreets()
  • Resolves: gh#2988 cron: enable inactive relations which are invalid
  • Resolves: gh#3018 additional streets is now available in gpx format as well

7.5

  • New /missing-housenumbers/.../view-result.json endpoint, exposing the missing-housenumbers analysis result for a relation in a machine-readable format.
  • New /additional-housenumbers/.../view-result.json endpoint, exposing the additional-housenumbers analysis result for a relation in a machine-readable format.
  • Resolves: gh#2592 cron now creates state for new, inactive relations
  • Resolves: gh#2628 rename *.expected-data to *.overpassql

7.4

  • Ported to chartjs v3, the javascript bundle size is now about 20% smaller

7.3

  • Ported to Rust, the missing-housenumbers analysis is now about 5 times faster
  • Resolves: gh#1664 re-try overpass when response is XML (an error message) and CSV was requested
  • Resolves: gh#1740 fix /filter-for/refcounty/../refsettlement/.. filtering out everything
  • Resolves: gh#1746 fix disappeared localized strings
  • Resolves: gh#1815 validator now rejects trailing whitespace when converting numbers to strings
  • Resolves: gh#1839 stats: report per-zip coverage of housenumbers
  • Resolves: gh#1950 fix the validator's exit code to actually fail on validation errors during CI
  • Resolves: gh#2009 stats: separate progressbar for the capital

7.2

  • Resolves: gh#964 get_street_from_housenumber: consider addr:place when addr:street is empty

  • Resolves: gh#978 gettext: check wsgi_additional.py for l10n strings

  • Resolves: gh#1008 invalid refstreets: add to missing/additional streets as well

  • Resolves: gh#1025 invalid refstreets: add UI to list all problematic relations

  • Resolves: gh#1033 missing streets: let "update from osm" also update house numbers

  • Resolves: gh#1048 invalid-relations: flag filter key names which are not OSM street names

  • Resolves: gh#1067 invalid refstreets: don't show note when there are no results

  • Resolves: gh#1126 stats, invalid-relations: ignore relation without an osm street list

  • Resolves: gh#1198 tests: fix missing test_config.TestCase base class

  • Resolves: gh#1206 Cache missing-housenumbers plain text output

  • Resolves: gh#1201 parse_access_log: ignore complete areas harder

  • Resolves: gh#1209 cron: also cache localized missing housenumbers html output

  • Resolves: gh#1225 missing-housenumbers html cache: handle missing relation yaml

  • Resolves: gh#1408 Fix Hungarian sort order

  • Resolves: gh#1463 stats: handle missing files in get_topcities()

  • Resolves: gh#1508 webframe: context time is in utc, datetime.date.fromtimestamp takes a local time

7.1

  • Resolves: gh#576 garbage collection: if a relation was created <1 month ago, then ignore it in parse_access_log.py

  • Resolves: gh#578 garbage collection: exclude completed areas

  • Resolves: gh#585 fix street mapping when street name contains commas

  • Resolves: gh#594 stats: top cities: filter for valid cities + captial districts

  • Resolves: gh#611 additional streets, opposite of missing streets are now searched for

  • Resolves: gh#613 provide a robots.txt

  • Resolves: gh#616 handle house numbers containing commas

  • Resolves: gh#617 garbage collection: filter out search engine user agents

  • Resolves: gh#688 reworked the toolbar

  • Resolves: gh#689 additional streets: improved main page, showing # of additional streets there

  • Resolves: gh#690 you can now filter for relations based on your current position

  • Resolves: gh#754 missing housenumbers now ignores points without house numbers and conscriptionnumbers

  • Resolves: gh#759 main page: default to hiding complete areas

  • Resolves: gh#765 calls to overpass now always redirect back to the original page

  • Resolves: gh#782 additional streets: handle streets with type=relation

  • Resolves: gh#784 street mappings: invalid osm or reference names are now flagged on the missing housenumbers page

  • Resolves: gh#830 additional streets: track OSM id of street names which come from house numbers

  • Resolves: gh#861 additional streets now can generate an overpass query

  • Resolves: gh#875 missing and additional streets now also have plain text and checklist output formats

  • Resolves: gh#886 additional streets have a proper last update date

7.0

  • Resolves: gh#564 wsgi: add support for locale-aware percent formatting

  • Resolves: gh#563 city progress: avoid double link

  • Resolves: gh#561 cron: update ref.count from reference_citycounts

  • Resolves: gh#521 sync missing-housenumbers and missing-streets toolbars

  • Resolves: gh#513 add overpass query for not yet audited street mappings

  • Resolves: gh#490 stats: add per-city coverage page

  • Resolves: gh#489 stats: add per-city monthly diff page

  • The stats feature now tracks the count of house number editors over time

6.0

  • The stats feature now has localized strings

  • The stats feature now has good test coverage

  • Resolves: gh#441 cron can now again update inactive relations once a month

  • Resolves: gh#437 the locale install requirement is now documented

  • Resolves: gh#436 ref street name is now shown after the osm street name if it differs

  • Resolves: gh#381 stats now has trendlines

  • Resolves: gh#426 missing housenumbers now allows updating from OSM with a single click

  • Resolves: gh#383 stats, monthly total now has an extra column to show today's count

  • Resolves: gh#385 invalid list items are now normalized by default

  • Resolves: gh#388 cron's console output and log file now uses the same format

  • Resolves: gh#414 the missing housenumber page's table is now sorted correctly even if house number ranges reduce the amount of items for some streets

  • Resolves: gh#372 commercial house numbers now can have comments, visible as tooltips

5.0

  • A new /osm/housenumber-stats/whole-country/ page featuring new and all-time house number data

  • A new cherry.py glue layer to help running on top of CherryPy

  • gh#380 the validator now catches strings which are not valid items in an invalid: string list

  • gh#363 next to letter suffixes (42/a), now digit suffixes are also accepted (42/1). Both are still limited to a single-char suffix (2020-03-23)

4.0

  • gh#344 next to the existing "txt" output, a new "chkl" output is available for missing house numbers of a relation, providing a plain text checklist. (2020-03-07)

  • yaml files are now parsed build-time to improve performance (main page loads 7 times faster)

  • complete line coverage for the cron code, which was the last uncovered module

3.0

  • complete test coverage for the wsgi code

2.0

  • gh#322 alias names are now supported for relations, so compatibility (with existing bookmarks) does not break when renaming. (2019-01-10)

  • gh#291 added error handling for not valid relation names. (2019-12-12)

  • gh#285: HTML output uses 42/A style for letter-suffixed house numbers, but plain text output uses 42a to help turning the output into invalid configs. (2019-12-06)

  • gh#267: it is now possible to opt in for a more strict behavior where 42/B is not considered mapped when 42/A is already mapped. (2019-11-29)

  • gh#269: noise in the reference can be now cleaned by filtering out house numbers explicitly, rather than filtering for valid ranges. (2019-11-15)

  • gh#195: track what source range generated what house numbers for more compact results. (2019-11-10)

  • gh#224: a way to generate the gpx of all streets missing house numbers. (2019-10-31)

  • gh#237: make OSM IDs of existing house numbers clickable. (2019-10-22)

  • gh#228: added time internal hint when the overpass query errors out due to not waiting enough. (2019-10-12)

  • gh#204: added possibility to list certain reftelepules names when a specific refmegye is selected on the main page. (2019-10-09)

1.0

  • Initial release

Enhancements up to 2019-10-07 were presented at https://www.meetup.com/OpenStreetMap-Hungary/.

Contributors

Here is a list of the contributors who have helped improving osm-gimmisn. Big shout-out to them!

If you feel you're missing from this list, feel free to add yourself in a PR.

Contributing

osm-gimmisn is free and open source. You can find the source code on GitHub and issues and feature requests can be posted on the issue tracker. If you'd like to contribute, please consider opening a pull request.

Reporting issues

You can file a new issue (bugreport, feature request) at GitHub. Please always describe a single problem.

Here is a minimal template:

Reproducer steps

  1. First step
  2. Second step
  3. Third step

Actual result

A description of what happens currently.

Expected behavior

A description of what you expected to happen.