public-transport.lua 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  1. -- Määrame skeemi keskkonnamuutujast või vaikimisi 'public'
  2. local schema = os.getenv("PGSCHEMA") or "public"
  3. -- This config example file is released into the Public Domain.
  4. -- This file shows how to use multi-stage processing to bring tags from
  5. -- public transport relations into member nodes and ways. This allows
  6. -- advanced processing of public transport networks including stops.
  7. -- Nodes tagged as public transport stops are imported into the 'stops' table,
  8. -- if they are part of a public transport relation. Ways tagged as highway or
  9. -- railway or imported into the 'lines' table. The public transport routes
  10. -- themselves will be in the 'routes' table, but without any geometry. As a
  11. -- "bonus" public transport stop area relations will be imported into the
  12. -- 'stop_areas' table.
  13. --
  14. -- For the 'stops' and 'lines' table two-stage processing is used. The
  15. -- 'rel_refs' text column will contain a list of all ref tags found in parent
  16. -- relations with type=route and route=public_transport. The 'rel_ids' column
  17. -- will be an integer array containing the relation ids. These could be used,
  18. -- for instance, to look up other relation tags from the 'routes' table.
  19. local tables = {}
  20. tables.stops = osm2pgsql.define_node_table('pt_stops', {
  21. { column = 'tags', type = 'jsonb' },
  22. { column = 'rel_refs', type = 'text' }, -- for the refs from the relations
  23. { column = 'rel_ids', sql_type = 'int8[]' }, -- array with integers (for relation IDs)
  24. { column = 'geom', type = 'point', not_null = true },
  25. }, {
  26. schema = schema,
  27. proj = 3301
  28. })
  29. tables.lines = osm2pgsql.define_way_table('pt_lines', {
  30. { column = 'tags', type = 'jsonb' },
  31. { column = 'rel_refs', type = 'text' }, -- for the refs from the relations
  32. { column = 'rel_ids', sql_type = 'int8[]' }, -- array with integers (for relation IDs)
  33. { column = 'geom', type = 'linestring', not_null = true },
  34. }, {
  35. schema = schema,
  36. proj = 3301
  37. })
  38. -- Tables don't have to have a geometry column
  39. tables.routes = osm2pgsql.define_relation_table('pt_routes', {
  40. { column = 'ref', type = 'text' },
  41. { column = 'type', type = 'text' },
  42. { column = 'from', type = 'text' },
  43. { column = 'to', type = 'text' },
  44. { column = 'tags', type = 'jsonb' },
  45. }, {
  46. schema = schema,
  47. proj = 3301
  48. })
  49. -- Stop areas contain everything belonging to a specific public transport
  50. -- stop. We model them here by adding a center point as geometry plus the
  51. -- radius of a circle that contains everything in that stop.
  52. tables.stop_areas = osm2pgsql.define_relation_table('pt_stop_areas', {
  53. { column = 'tags', type = 'jsonb' },
  54. { column = 'radius', type = 'real', not_null = true },
  55. { column = 'geom', type = 'point', not_null = true },
  56. }, {
  57. schema = schema,
  58. proj = 3301
  59. })
  60. -- This will be used to store information about relations queryable by member
  61. -- node/way id. These are table of tables. The outer table is indexed by the
  62. -- node/way id, the inner table indexed by the relation id. This way even if
  63. -- the information about a relation is added twice, it will be in there only
  64. -- once. It is always good to write your osm2pgsql Lua code in an idempotent
  65. -- way, i.e. it can be called any number of times and will lead to the same
  66. -- result.
  67. local n2r = {}
  68. local w2r = {}
  69. local function unique_array(array)
  70. local result = {}
  71. local last = nil
  72. for _, v in ipairs(array) do
  73. if v ~= last then
  74. result[#result + 1] = v
  75. last = v
  76. end
  77. end
  78. return result
  79. end
  80. local separator = '·' -- use middle dot as separator character
  81. local function add_rel_data(row, d)
  82. if not d then
  83. return
  84. end
  85. local refs = {}
  86. local ids = {}
  87. for rel_id, rel_ref in pairs(d) do
  88. refs[#refs + 1] = rel_ref
  89. ids[#ids + 1] = rel_id
  90. end
  91. table.sort(refs)
  92. table.sort(ids)
  93. row.rel_refs = table.concat(unique_array(refs), separator)
  94. row.rel_ids = '{' .. table.concat(unique_array(ids), ',') .. '}'
  95. end
  96. function osm2pgsql.process_node(object)
  97. -- We are only interested in public transport stops here, and they are
  98. -- only available in the second stage.
  99. if osm2pgsql.stage ~= 2 then
  100. return
  101. end
  102. local row = {
  103. tags = object.tags,
  104. geom = object:as_point()
  105. }
  106. -- If there is any data from parent relations, add it in
  107. add_rel_data(row, n2r[object.id])
  108. tables.stops:insert(row)
  109. end
  110. function osm2pgsql.process_way(object)
  111. -- We are only interested in highways and railways
  112. if not object.tags.highway and not object.tags.railway then
  113. return
  114. end
  115. -- Data we will store in the 'lines' table always has the tags from
  116. -- the way
  117. local row = {
  118. tags = object.tags,
  119. geom = object:as_linestring()
  120. }
  121. -- If there is any data from parent relations, add it in
  122. add_rel_data(row, w2r[object.id])
  123. tables.lines:insert(row)
  124. end
  125. local pt = {
  126. bus = true,
  127. light_rail = true,
  128. subway = true,
  129. tram = true,
  130. trolleybus = true,
  131. }
  132. -- We are only interested in certain route relations with a ref tag
  133. local function wanted_relation(tags)
  134. return tags.type == 'route' and pt[tags.route] and tags.ref
  135. end
  136. -- This function is called for every added, modified, or deleted relation.
  137. -- Its only job is to return the ids of all member nodes/ways of the specified
  138. -- relation we want to see in stage 2 again. It MUST NOT store any information
  139. -- about the relation!
  140. function osm2pgsql.select_relation_members(relation)
  141. -- Only interested in public transport relations with refs
  142. if wanted_relation(relation.tags) then
  143. local node_ids = {}
  144. local way_ids = {}
  145. for _, member in ipairs(relation.members) do
  146. if member.type == 'n' and member.role == 'stop' then
  147. node_ids[#node_ids + 1] = member.ref
  148. elseif member.type == 'w' and member.role == '' then
  149. way_ids[#way_ids + 1] = member.ref
  150. end
  151. end
  152. return {
  153. nodes = node_ids,
  154. ways = way_ids,
  155. }
  156. end
  157. end
  158. -- The process_relation() function should store all information about relation
  159. -- members that might be needed in stage 2.
  160. function osm2pgsql.process_relation(object)
  161. if object.tags.type == 'public_transport' and object.tags.public_transport == 'stop_area' then
  162. local x1, y1, x2, y2 = object:as_geometrycollection():transform(3857):get_bbox()
  163. local radius = math.sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1))
  164. tables.stop_areas:insert({
  165. tags = object.tags,
  166. geom = object:as_geometrycollection():centroid(),
  167. radius = radius,
  168. })
  169. return
  170. end
  171. if wanted_relation(object.tags) then
  172. tables.routes:insert({
  173. type = object.tags.route,
  174. ref = object.tags.ref,
  175. from = object.tags.from,
  176. to = object.tags.to,
  177. tags = object.tags,
  178. })
  179. -- Go through all the members and store relation ids and refs so they
  180. -- can be found by the member node/way id.
  181. for _, member in ipairs(object.members) do
  182. if member.type == 'n' then
  183. if not n2r[member.ref] then
  184. n2r[member.ref] = {}
  185. end
  186. n2r[member.ref][object.id] = object.tags.ref
  187. elseif member.type == 'w' then
  188. if not w2r[member.ref] then
  189. w2r[member.ref] = {}
  190. end
  191. w2r[member.ref][object.id] = object.tags.ref
  192. end
  193. end
  194. end
  195. end