1 # SPDX-License-Identifier: GPL-2.0-only
3 # This file is part of Nominatim. (https://nominatim.org)
5 # Copyright (C) 2022 by the Nominatim developer community.
6 # For a full list of authors see the git log.
7 from pathlib import Path
10 class GeometryFactory:
11 """ Provides functions to create geometries from scenes and data grids.
15 defpath = Path(__file__) / '..' / '..' / '..' / 'scenes' / 'data'
16 self.scene_path = os.environ.get('SCENE_PATH', defpath.resolve())
20 def parse_geometry(self, geom, scene):
21 """ Create a WKT SQL term for the given geometry.
22 The function understands the following formats:
25 Geometry from a scene. If the scene is omitted, use the
34 <P> may either be a coordinate of the form '<x> <y>' or a single
35 number. In the latter case it must refer to a point in
36 a previously defined grid.
38 if geom.find(':') >= 0:
39 return "ST_SetSRID({}, 4326)".format(self.get_scene_geometry(scene, geom))
41 if geom.find(',') < 0:
42 out = "POINT({})".format(self.mk_wkt_point(geom))
43 elif geom.find('(') < 0:
44 out = "LINESTRING({})".format(self.mk_wkt_points(geom))
46 out = "POLYGON(({}))".format(self.mk_wkt_points(geom.strip('() ')))
48 return "ST_SetSRID('{}'::geometry, 4326)".format(out)
50 def mk_wkt_point(self, point):
51 """ Parse a point description.
52 The point may either consist of 'x y' cooordinates or a number
53 that refers to a grid setup.
56 if geom.find(' ') >= 0:
60 pt = self.grid_node(int(geom))
62 assert False, "Scenario error: Point '{}' is not a number".format(geom)
64 assert pt is not None, "Scenario error: Point '{}' not found in grid".format(geom)
65 return "{} {}".format(*pt)
67 def mk_wkt_points(self, geom):
68 """ Parse a list of points.
69 The list must be a comma-separated list of points. Points
70 in coordinate and grid format may be mixed.
72 return ','.join([self.mk_wkt_point(x) for x in geom.split(',')])
74 def get_scene_geometry(self, default_scene, name):
75 """ Load the geometry from a scene.
78 for obj in name.split('+'):
80 if oname.startswith(':'):
81 assert default_scene is not None, "Scenario error: You need to set a scene"
82 defscene = self.load_scene(default_scene)
83 wkt = defscene[oname[1:]]
85 scene, obj = oname.split(':', 2)
86 scene_geoms = self.load_scene(scene)
87 wkt = scene_geoms[obj]
89 geoms.append("'{}'::geometry".format(wkt))
94 return 'ST_LineMerge(ST_Collect(ARRAY[{}]))'.format(','.join(geoms))
96 def load_scene(self, name):
97 """ Load a scene from a file.
99 if name in self.scene_cache:
100 return self.scene_cache[name]
103 with open(Path(self.scene_path) / "{}.wkt".format(name), 'r') as fd:
106 obj, wkt = line.split('|', 2)
107 scene[obj.strip()] = wkt.strip()
108 self.scene_cache[name] = scene
112 def set_grid(self, lines, grid_step):
113 """ Replace the grid with one from the given lines.
121 self.grid[int(pt_id)] = (x, y)
125 def grid_node(self, nodeid):
126 """ Get the coordinates for the given grid node.
128 return self.grid.get(nodeid)