Minimal analyzer based on SQL query

The kind of analyzer based on SQL query is relevant when want to check geometry or relation between multiple objects. The result of the query is used to fill the Osmose issue report.

For explenation purpose only, we just here make an analyzer that report pharmacy as node inside a building polygon, it is not looking for issue in the data.

SELECT
    -- We report pharmacy nodes osm id and location
    nodes.id,
    ST_AsText(nodes.geom) AS geom
FROM
    nodes
    JOIN ways ON
        -- Use the index on tags
        ways.tags != ''::hstore AND
        -- Look for ways with valid building tag
        ways.tags?'building' AND ways.tags->'building' != 'no' AND
        -- Look for way as valid polygon
        is_polygon AND
        -- Use the spatial index for ways bbox crossing the node location
        ways.linestring && nodes.geom AND
        -- Ensure the node is inside the polygon
        -- (ST_Intersects call it self the spatial crossing with bbox, so in this case it not necessary)
        ST_Intersects(ST_MakePolygon(ways.linestring), nodes.geom)
WHERE
    -- Use the index on tags
    nodes.tags != ''::hstore AND
    -- Look for node with tag amenity=pharmacy
    nodes.tags?'amenity' AND nodes.tags->'amenity' = 'pharmacy'

In [1]:
sql10 = """
SELECT
    nodes.id,
    ST_AsText(nodes.geom) AS geom
FROM
    nodes
    JOIN ways ON
        ways.tags != ''::hstore AND
        ways.tags?'building' AND ways.tags->'building' != 'no' AND
        is_polygon AND
        ST_Intersects(ST_MakePolygon(ways.linestring), nodes.geom)
WHERE
    nodes.tags != ''::hstore AND
    nodes.tags?'amenity' AND nodes.tags->'amenity' = 'pharmacy'
"""

We have to create an inherited class from Analyser_Osmosis. The __init__() setup the meta information of produced issues. It defines a class id for Osmose issues.

analyser_osmosis_common() run the query and build the Osmose issues. For each row returned by the query, an Osmose issue is created using the lambda function. It should at least return:

  • class refer to the class id definition,
  • data: must match the result row definition from the query.

In [2]:
%cd "/opt/osmose-backend/"
from analysers.Analyser_Osmosis import Analyser_Osmosis

class Analyser_Pharmacy_Building(Analyser_Osmosis):
    def __init__(self, config, logger = None):
        super().__init__(config, logger)

        # Define Osmose issue class id 1
        self.classs[1] = self.def_class(
            item = 2010,
            level = 1,
            tags = ['building'],
            title = T_('Pharmacy node in Building')
        )

    def analyser_osmosis_common(self):
        # Run the SQL query
        self.run(sql10, lambda res: {
            # For each result, create an osmose issue of class 1
            'class': 1,
            # Explain how to interpret the returned fields from query
            'data': [self.node_full, self.positionAsText]
        })


/opt/osmose-backend

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'}

In [7]:
from modules.jupyter import *

csv = run(country_conf, Analyser_Pharmacy_Building, format = 'csv')
print_csv(csv)


Out[7]:
classs subclass ids types text lon lat fix
0 1 NaN [280489587, 'POINT(7.4284058 43.743628)'] ['node', None] NaN 7.428406 43.743628 NaN
1 1 NaN [954714337, 'POINT(7.421026 43.7355109)'] ['node', None] NaN 7.421026 43.735511 NaN
2 1 NaN [1712696734, 'POINT(7.4187526 43.7328208)'] ['node', None] NaN 7.418753 43.732821 NaN
3 1 NaN [1712696815, 'POINT(7.418612 43.7340868)'] ['node', None] NaN 7.418612 43.734087 NaN
4 1 NaN [1872534072, 'POINT(7.4190088 43.7327161)'] ['node', None] NaN 7.419009 43.732716 NaN
5 1 NaN [3087473845, 'POINT(7.4240744 43.7398297)'] ['node', None] NaN 7.424074 43.739830 NaN
6 1 NaN [3258168663, 'POINT(7.4250704 43.7417064)'] ['node', None] NaN 7.425070 43.741706 NaN
7 1 NaN [3258168664, 'POINT(7.425963 43.7422749)'] ['node', None] NaN 7.425963 43.742275 NaN
8 1 NaN [3258168665, 'POINT(7.4386332 43.7519871)'] ['node', None] NaN 7.438633 43.751987 NaN
9 1 NaN [3258168666, 'POINT(7.4242094 43.7422016)'] ['node', None] NaN 7.424209 43.742202 NaN
10 1 NaN [3258168667, 'POINT(7.4253145 43.7426366)'] ['node', None] NaN 7.425315 43.742637 NaN
11 1 NaN [3258172411, 'POINT(7.4164909 43.7388732)'] ['node', None] NaN 7.416491 43.738873 NaN
12 1 NaN [3258188564, 'POINT(7.4109356 43.7270861)'] ['node', None] NaN 7.410936 43.727086 NaN
13 1 NaN [4437127907, 'POINT(7.4168604 43.7371994)'] ['node', None] NaN 7.416860 43.737199 NaN
14 1 NaN [5080982994, 'POINT(7.4227385 43.7309421)'] ['node', None] NaN 7.422738 43.730942 NaN
15 1 NaN [6696261625, 'POINT(7.4222121 43.7311039)'] ['node', None] NaN 7.422212 43.731104 NaN
16 1 NaN [7111828563, 'POINT(7.416616 43.7327117)'] ['node', None] NaN 7.416616 43.732712 NaN

In [8]:
geojson = run(country_conf, Analyser_Pharmacy_Building, format = 'geojson')
print_geojson(geojson, limit = 100)



In [ ]: