MapCSS Plugin

Like the Plugins, the MapCSS are dedicated to check the tags of one object at the time.

The MapCSS is more easy to understand and write than the Python implementation. The definition of the MapCSS language is from JOSM. The MapCSS code can also be run and shared with the JOSM validator.

For explanation purpose only, we just here make a plugin that report fountains, it is not looking for issue in the data.

Each MapCSS file must begin by a set of general declaration named meta.

Note 1, We are in a Python Jupyter note book, so we need to quote the MapCSS code into Python String. But it does not have to be so in a plain MapCSS file.

Note 2, the declarations stating with -osmose are specific to Osmose-QA and ignored by JOSM.


In [1]:
%cd "/opt/osmose-backend/"
mapcss_code = """
meta {
    title: "Osmose-QA – Object here";
    description: "Nice report.";
    author: "Bob";
    min-josm-version: 14481;
    -osmoseTags: list("tag");
}
meta[lang=en] { /* lang=en, unused, only to use tr() to catch string for translation */
    description: tr("Nice report.");
}
meta[lang=fr] {
    description: "Chouette rapport.";
}
"""


/opt/osmose-backend

Rules are written like CSS for HTML, but on OSM Objects. You must select type of objects, one of:

  • node,
  • way,
  • relation,
  • (area is not support in Osmose-QA implementation),
  • or all using *.

The condition must be applied to filter objects, use as many conditions as required: [amenity=fountain].

Once objects are selected, Osmose-QA issues are yield using one throwError or throwWarning. throwError is by default mapped to Osmose-QA level of issue 2, and throwWarning to level 3. JOSM also define throwOther, but is ignored by Osmose-QA.


In [2]:
mapcss_code = """
*[amenity=fountain] {
    throwError: tr("Fountain here");
}
"""

Osmose-QA specific declaration can be added to match the Osmose-QA issue item, class, level and tags.


In [3]:
mapcss_code = """
*[amenity=fountain] {
    throwWarning: tr("Fountain here");
    -osmoseItemClassLevel: "4030/40301/2";
    -osmoseTags: list("fix:survey"); // The tags are added to the on of the global declaration
}
"""

Unitary tests should be set to validate the behaviors of the rule. Set as many asserts as required.

  • assertMatch: should define an object that will match the rule.
  • assertNoMatch: should define an object that will not match the rule.

The rule is run with all objects defined in assertMatch and assertNoMatch to ensure the expected behaviors.


In [4]:
mapcss_code = """
*[amenity=fountain] {
    throwError: tr("Fountain here");
    
    assertMatch: "way amenity=fountain";
    assertMatch: "way amenity=fountain name='Eau Claire'";
    assertNoMatch: "node sport=boules";
}
"""

Then we can convert the MapCSS code to Python. Using this command line to produce a Fountain.py file, from the root directory:

python -m mapcss.mapcss2osmose plugins/Fountain.validator.mapcss

But here, we compile the MapCSS programmatically.


In [5]:
from modules.jupyter import *

# Define the Fountain Python plugin, and it Test class
compiled = compile_mapcss(mapcss_code, 'Fountain')

# Run the test
compiled.Test().test() # Returns nothing where it is OK, else error.

To run the analyze we need a context of execution. Each country or area have a entry in the file osmose_config.py.


In [6]:
import osmose_config as config

country_conf = config.config['monaco']
country_conf.init()

country_conf.analyser_options


Out[6]:
{'project': 'openstreetmap',
 'country': 'MC',
 'language': 'fr',
 'proj': 2154,
 'phone_code': '377',
 'phone_len': 8,
 'phone_format': '^[+]%s([- ./]*[469])([- ./]*[0-9]){6}[0-9]$',
 'phone_international': '00'}

The plugins are run by the analyzer "sax". The result can be fetched by Jupyter and displayed. By default it is in Osmose-QA XML format. CSV en GeoJson format are for debug only and have partial content.


In [7]:
from analysers.analyser_sax import Analyser_Sax
from modules.jupyter import *

csv = run(country_conf, Analyser_Sax, plugin = compiled.Fountain, format = 'csv')
print_csv(csv)


Out[7]:
classs subclass ids types text lon lat fix
0 1 709612454 [456295834] ['node'] Fountain here 7.422254 43.731620 NaN
1 1 709612454 [4065627219] ['node'] Fountain here 7.421072 43.728590 NaN
2 1 709612454 [5915727561] ['node'] Fountain here 7.412227 43.725283 NaN
3 1 709612454 [5918146876] ['node'] Fountain here 7.419279 43.727257 NaN
4 1 709612454 [5918204005] ['node'] Fountain here 7.413440 43.726025 NaN
5 1 709612454 [5918204081] ['node'] Fountain here 7.418953 43.726833 NaN
6 1 709612454 [6123590359] ['node'] Fountain here 7.417662 43.725323 NaN
7 1 709612454 [6696261621] ['node'] Fountain here 7.423629 43.730971 NaN
8 1 709612454 [572933762] ['way'] Fountain here 7.419157 43.732581 NaN
9 1 709612454 [572935477] ['way'] Fountain here 7.426977 43.739671 NaN
10 1 709612454 [572935479] ['way'] Fountain here 7.427476 43.739418 NaN
11 1 709612454 [572938550] ['way'] Fountain here 7.430044 43.741546 NaN
12 1 709612454 [572947839] ['way'] Fountain here 7.434892 43.747410 NaN
13 1 709612454 [572948182] ['way'] Fountain here 7.438353 43.749129 NaN
14 1 709612454 [572948695] ['way'] Fountain here 7.438335 43.747669 NaN
15 1 709612454 [686461248] ['way'] Fountain here 7.416823 43.730711 NaN
16 1 709612454 [686461249] ['way'] Fountain here 7.416799 43.730598 NaN
17 1 709612454 [686461252] ['way'] Fountain here 7.416663 43.730625 NaN
18 1 709612454 [686461253] ['way'] Fountain here 7.416713 43.730753 NaN
19 1 709612454 [686461255] ['way'] Fountain here 7.416779 43.730590 NaN
20 1 709612454 [688300839] ['way'] Fountain here 7.416750 43.730519 NaN
21 1 709612454 [688300840] ['way'] Fountain here 7.416842 43.730533 NaN
22 1 709612454 [778494661] ['way'] Fountain here 7.425755 43.740410 NaN
23 1 709612454 [778494663] ['way'] Fountain here 7.426416 43.739964 NaN

In [8]:
geojson = run(country_conf, Analyser_Sax, plugin = compiled.Fountain, format = 'geojson')
print_geojson(geojson)



In [ ]: