highways.lua 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351
  1. -- NOTE: Beautification tool not available. Original content preserved.
  2. -- Määrame skeemi keskkonnamuutujast või vaikimisi 'public'
  3. local schema = os.getenv("PGSCHEMA") or "public"
  4. local tables = {}
  5. -- Define the "countries" locator and get all country geometries from the
  6. -- database. Use the import-countries.lua file to import them first, before
  7. -- you run this.
  8. local countries = osm2pgsql.define_locator({name = "countries"})
  9. countries:add_from_db("SELECT code, ST_Subdivide(geom, 200) FROM " .. schema .. ".countries")
  10. tables.highways =
  11. osm2pgsql.define_way_table(
  12. "highways",
  13. {
  14. -- Add a SERIAL column and tell osm2pgsql not to fill it (PostgreSQL will
  15. -- do that for us)
  16. {column = "id", sql_type = "serial", create_only = true},
  17. -- We always need a highway type, so we can declare the column as NOT NULL
  18. {column = "hwtype", type = "text", not_null = true},
  19. {column = "service", type = "text"},
  20. {column = "access", type = "text"},
  21. {column = "country", type = "text"},
  22. {column = "colour", type = "text"},
  23. {column = "name", type = "text"},
  24. {column = "tags", type = "jsonb"},
  25. {column = "surface", type = "text"},
  26. -- type "direction" is special, see below
  27. {column = "oneway", type = "direction"},
  28. {column = "maxspeed", type = "int"},
  29. -- type "bool" is special, see below
  30. {column = "lit", type = "bool"},
  31. -- osm2pgsql doesn't know about PostgreSQL arrays, so we define the SQL
  32. -- type of this column and then have to convert our array data into a
  33. -- valid text representation for that type, see below.
  34. {column = "nodes", sql_type = "int8[]"},
  35. {column = "rel_refs", type = "int"}, -- for the refs from the relations
  36. {column = "rel_ids", sql_type = "int8[]"}, -- array with integers (for relation IDs),
  37. {column = "geom", type = "linestring", not_null = true}
  38. },
  39. {
  40. schema = schema,
  41. proj = 3301
  42. }
  43. )
  44. -- Tables don't have to have a geometry column
  45. tables.routes =
  46. osm2pgsql.define_relation_table(
  47. "routes",
  48. {
  49. {column = "ref", type = "int"}, -- for the refs from the relations
  50. {column = "tags", type = "jsonb"}
  51. },
  52. {
  53. schema = schema,
  54. proj = 3301
  55. }
  56. )
  57. -- Helper function to remove some of the tags we usually are not interested in.
  58. -- Something like this can be useful if you are writing all tags to the
  59. -- database in a JSON(B) column and don't want that cluttered with lots of tags
  60. -- nobody cares about. Returns true if there are no tags left.
  61. local function clean_tags(tags)
  62. local keys_to_remove = {
  63. ""
  64. }
  65. for _, key in ipairs(keys_to_remove) do
  66. tags[key] = nil
  67. end
  68. -- Mittevajalike tag'ide eemaldamine
  69. local function hide_tags(tags)
  70. local keys_to_hide = {
  71. "odbl",
  72. "created_by",
  73. "source",
  74. "source:ref",
  75. "source:addr",
  76. "addr:country",
  77. "addr:city",
  78. "addr:street",
  79. "addr:housenumber",
  80. "addr:housename",
  81. "addr:postcode",
  82. "height",
  83. "building",
  84. "maaamet:ETAK",
  85. "building:use",
  86. "building:levels",
  87. "old_addr:street",
  88. "old_addr:housenumber",
  89. "source:addr:2023",
  90. "maaamet:orig_tunnus",
  91. "wikidata",
  92. "old_addr:housename",
  93. "old_addr:place",
  94. "name",
  95. "phone",
  96. "internet_access",
  97. "wheelchair",
  98. "roof:shape",
  99. "name:en",
  100. "name:ru",
  101. "brand",
  102. "fuel",
  103. "website",
  104. "alt_name",
  105. "capacity",
  106. "operator",
  107. "fuel:diesel",
  108. "self_service",
  109. "opening_hours",
  110. "brand:wikidata",
  111. "fuel:octane_95",
  112. "fuel:octane_98",
  113. "brand:wikipedia",
  114. "roof:material",
  115. "email",
  116. "name:et",
  117. "roof",
  118. "stars",
  119. "fee",
  120. "old_name",
  121. "name:fi",
  122. "description:ru",
  123. "url",
  124. "name:lt",
  125. "addr:place",
  126. "roof:levels",
  127. "addr:name",
  128. "name:pl",
  129. "name:lv",
  130. "payment:credit_cards",
  131. "payment:debit_cards",
  132. "amenity",
  133. "tourism",
  134. "shop",
  135. "man_made",
  136. "addr:old_street",
  137. "operator:wikidata",
  138. "fuel:adblue",
  139. "contact:email",
  140. "leisure",
  141. "contact:phone",
  142. "contact:website",
  143. "healthcare",
  144. "maaamet:ADS",
  145. "name:vro",
  146. "check_date",
  147. "building:material",
  148. "owner:ru",
  149. "int_name",
  150. "wikipedia",
  151. "power",
  152. "fuel:HGV_diesel",
  153. "branch",
  154. "EE:EHIS:id",
  155. "operator:type",
  156. "operator:type",
  157. "layer"
  158. }
  159. for _, key in ipairs(keys_to_hide) do
  160. tags[key] = nil
  161. end
  162. return next(tags) == nil
  163. end
  164. return next(tags) == nil
  165. end
  166. local highway_types = {
  167. "motorway",
  168. "motorway_link",
  169. "trunk",
  170. "trunk_link",
  171. "primary",
  172. "primary_link",
  173. "secondary",
  174. "secondary_link",
  175. "tertiary",
  176. "tertiary_link",
  177. "unclassified",
  178. "residential",
  179. "track",
  180. "service"
  181. }
  182. -- Prepare table "types" for quick checking of highway types
  183. local types = {}
  184. for _, k in ipairs(highway_types) do
  185. types[k] = 1
  186. end
  187. -- Parse a maxspeed value like "30" or "55 mph" and return a number in km/h
  188. local function parse_speed(input)
  189. if not input then
  190. return nil
  191. end
  192. local maxspeed = tonumber(input)
  193. -- If maxspeed is just a number, it is in km/h, so just return it
  194. if maxspeed then
  195. return maxspeed
  196. end
  197. -- If there is an 'mph' at the end, convert to km/h and return
  198. if input:sub(-3) == "mph" then
  199. local num = tonumber(input:sub(1, -4))
  200. if num then
  201. return math.floor(num * 1.60934)
  202. end
  203. end
  204. return nil
  205. end
  206. -- Each country uses their own colour for motorways. Here is the beginning
  207. -- of a list of some countries in Europe. Source:
  208. -- https://en.wikipedia.org/wiki/Comparison_of_European_road_signs
  209. local cc2colour = {
  210. EE = "#2d00e5",
  211. RU = "#128044",
  212. LT = "#174688",
  213. LV = "#333b97"
  214. }
  215. -- This will be used to store information about relations queryable by member
  216. -- way id. It is a table of tables. The outer table is indexed by the way id,
  217. -- the inner table indexed by the relation id. This way even if the information
  218. -- about a relation is added twice, it will be in there only once. It is
  219. -- always good to write your osm2pgsql Lua code in an idempotent way, i.e.
  220. -- it can be called any number of times and will lead to the same result.
  221. local w2r = {}
  222. function osm2pgsql.process_way(object)
  223. -- Get the type of "highway" and remove it from the tags
  224. local highway = object.tags.highway
  225. -- We are only interested in highways of the given types
  226. if not types[highway] then
  227. return
  228. end
  229. -- Only with geometry
  230. local geom = object:as_linestring()
  231. if not geom then
  232. return
  233. end
  234. -- Only Estonia
  235. local cc = countries:first_intersecting(geom)
  236. if cc ~= "EE" then
  237. return
  238. end
  239. if highway then
  240. local hwtype = highway
  241. local service = object.tags.service
  242. local service = object.tags.access
  243. local colour = cc2colour[cc]
  244. -- We want to put the name in its own column
  245. local name = object.tags.name
  246. local tags = object.tags
  247. local surface = object.tags.surface
  248. -- The 'oneway' column has the special type "direction", which will
  249. -- store "yes", "true" and "1" as 1, "-1" as -1, and everything else
  250. -- as 0.
  251. local oneway = object.tags.oneway or 0 -- The 'maxspeed' column gets the maxspeed in km/h
  252. local maxspeed = parse_speed(object.tags.maxspeed)
  253. -- The 'lit' column has the special type "bool", which will store
  254. -- "yes" and "true" as true and everything else as false value.
  255. local lit = object.tags.lit
  256. -- The way node ids are put into a format that PostgreSQL understands
  257. -- for a column of type "int8[]".
  258. local nodes = "{" .. table.concat(object.nodes, ",") .. "}"
  259. -- Data we will store in the "highways" table always has the tags from
  260. -- the way
  261. local row = {
  262. hwtype = hwtype,
  263. service = service,
  264. access = access,
  265. country = cc,
  266. colour = colour,
  267. name = name,
  268. tags = tags,
  269. surface = surface,
  270. oneway = oneway,
  271. maxspeed = maxspeed,
  272. lit = lit,
  273. nodes = nodes,
  274. geom = geom
  275. }
  276. -- If there is any data from parent relations, add it in
  277. local d = w2r[object.id]
  278. if d then
  279. local refs = {}
  280. local ids = {}
  281. for rel_id, rel_ref in pairs(d) do
  282. refs[#refs + 1] = rel_ref
  283. ids[#ids + 1] = rel_id
  284. end
  285. table.sort(refs)
  286. table.sort(ids)
  287. row.rel_refs = table.concat(refs, ",")
  288. row.rel_ids = "{" .. table.concat(ids, ",") .. "}"
  289. end
  290. if clean_tags(object.tags) then
  291. return
  292. end
  293. --hide_tags(object.tags)
  294. tables.highways:insert(row)
  295. end
  296. end
  297. -- This function is called for every added, modified, or deleted relation.
  298. -- Its only job is to return the ids of all member ways of the specified
  299. -- relation we want to see in stage 2 again. It MUST NOT store any information
  300. -- about the relation!
  301. function osm2pgsql.select_relation_members(relation)
  302. -- Only interested in relations with type=route, route=road and a ref
  303. if relation.tags.type == "route" and relation.tags.route == "road" and relation.tags.ref then
  304. return {ways = osm2pgsql.way_member_ids(relation)}
  305. end
  306. end
  307. -- The process_relation() function should store all information about way
  308. -- members that might be needed in stage 2.
  309. function osm2pgsql.process_relation(object)
  310. if object.tags.type == "route" and object.tags.route == "road" and object.tags.ref then
  311. tables.routes:insert(
  312. {
  313. ref = object.tags.ref,
  314. tags = object.tags
  315. }
  316. )
  317. -- Go through all the members and store relation ids and refs so they
  318. -- can be found by the way id.
  319. for _, member in ipairs(object.members) do
  320. if member.type == "w" then
  321. if not w2r[member.ref] then
  322. w2r[member.ref] = {}
  323. end
  324. w2r[member.ref][object.id] = object.tags.ref
  325. end
  326. end
  327. end
  328. end