1-- This config example file is released into the Public Domain.
2
3-- This configuration for the flex output tries to be compatible with the
4-- original pgsql C transform output. There might be some corner cases but
5-- it should do exactly the same in almost all cases.
6
7-- The output projection used (3857, web mercator is the default). Set this
8-- to 4326 if you were using the -l|--latlong option or to the EPSG
9-- code you were using on the -E|-proj option.
10local srid = 3857
11
12-- Set this to true if you were using option -K|--keep-coastlines.
13local keep_coastlines = false
14
15-- Set this to the table name prefix (what used to be option -p|--prefix).
16local prefix = 'planet_osm'
17
18-- Set this to true if multipolygons should be written as multipolygons into
19-- db (what used to be option -G|--multi-geometry).
20local multi_geometry = false
21
22-- Set this to true if you want an hstore column (what used to be option
23-- -k|--hstore). Can not be true if "hstore_all" is true.
24local hstore = false
25
26-- Set this to true if you want all tags in an hstore column (what used to
27-- be option -j|--hstore-all). Can not be true if "hstore" is true.
28local hstore_all = false
29
30-- Only keep objects that have a value in one of the non-hstore columns
31-- (normal action with --hstore is to keep all objects). Equivalent to
32-- what used to be set through option --hstore-match-only.
33local hstore_match_only = false
34
35-- Set this to add an additional hstore (key/value) column containing all tags
36-- that start with the specified string, eg "name:". Will produce an extra
37-- hstore column that contains all "name:xx" tags. Equivalent to what used to
38-- be set through option -z|--hstore-column. Unlike the -z option which can
39-- be specified multiple time, this does only support a single additional
40-- hstore column.
41local hstore_column = nil
42
43-- There is some very old specialized handling of route relations in osm2pgsql,
44-- which you probably don't need. This is disabled here, but you can enable
45-- it by setting this to true. If you don't understand this, leave it alone.
46local enable_legacy_route_processing = false
47
48-- ---------------------------------------------------------------------------
49
50if hstore and hstore_all then
51    error("hstore and hstore_all can't be both true")
52end
53
54-- Used for splitting up long linestrings
55if srid == 4326 then
56    max_length = 1
57else
58    max_length = 100000
59end
60
61-- Ways with any of the following keys will be treated as polygon
62local polygon_keys = {
63    'aeroway',
64    'amenity',
65    'building',
66    'harbour',
67    'historic',
68    'landuse',
69    'leisure',
70    'man_made',
71    'military',
72    'natural',
73    'office',
74    'place',
75    'power',
76    'public_transport',
77    'shop',
78    'sport',
79    'tourism',
80    'water',
81    'waterway',
82    'wetland',
83    'abandoned:aeroway',
84    'abandoned:amenity',
85    'abandoned:building',
86    'abandoned:landuse',
87    'abandoned:power',
88    'area:highway'
89}
90
91-- Objects without any of the following keys will be deleted
92local generic_keys = {
93    'access',
94    'addr:housename',
95    'addr:housenumber',
96    'addr:interpolation',
97    'admin_level',
98    'aerialway',
99    'aeroway',
100    'amenity',
101    'area',
102    'barrier',
103    'bicycle',
104    'boundary',
105    'brand',
106    'bridge',
107    'building',
108    'capital',
109    'construction',
110    'covered',
111    'culvert',
112    'cutting',
113    'denomination',
114    'disused',
115    'ele',
116    'embankment',
117    'foot',
118    'generation:source',
119    'harbour',
120    'highway',
121    'historic',
122    'hours',
123    'intermittent',
124    'junction',
125    'landuse',
126    'layer',
127    'leisure',
128    'lock',
129    'man_made',
130    'military',
131    'motorcar',
132    'name',
133    'natural',
134    'office',
135    'oneway',
136    'operator',
137    'place',
138    'population',
139    'power',
140    'power_source',
141    'public_transport',
142    'railway',
143    'ref',
144    'religion',
145    'route',
146    'service',
147    'shop',
148    'sport',
149    'surface',
150    'toll',
151    'tourism',
152    'tower:type',
153    'tracktype',
154    'tunnel',
155    'water',
156    'waterway',
157    'wetland',
158    'width',
159    'wood',
160    'abandoned:aeroway',
161    'abandoned:amenity',
162    'abandoned:building',
163    'abandoned:landuse',
164    'abandoned:power',
165    'area:highway'
166}
167
168-- The following keys will be deleted
169local delete_keys = {
170    'attribution',
171    'comment',
172    'created_by',
173    'fixme',
174    'note',
175    'note:*',
176    'odbl',
177    'odbl:note',
178    'source',
179    'source:*',
180    'source_ref',
181    'way',
182    'way_area',
183    'z_order',
184}
185
186local point_columns = {
187    'access',
188    'addr:housename',
189    'addr:housenumber',
190    'addr:interpolation',
191    'admin_level',
192    'aerialway',
193    'aeroway',
194    'amenity',
195    'area',
196    'barrier',
197    'bicycle',
198    'brand',
199    'bridge',
200    'boundary',
201    'building',
202    'capital',
203    'construction',
204    'covered',
205    'culvert',
206    'cutting',
207    'denomination',
208    'disused',
209    'ele',
210    'embankment',
211    'foot',
212    'generator:source',
213    'harbour',
214    'highway',
215    'historic',
216    'horse',
217    'intermittent',
218    'junction',
219    'landuse',
220    'layer',
221    'leisure',
222    'lock',
223    'man_made',
224    'military',
225    'motorcar',
226    'name',
227    'natural',
228    'office',
229    'oneway',
230    'operator',
231    'place',
232    'population',
233    'power',
234    'power_source',
235    'public_transport',
236    'railway',
237    'ref',
238    'religion',
239    'route',
240    'service',
241    'shop',
242    'sport',
243    'surface',
244    'toll',
245    'tourism',
246    'tower:type',
247    'tunnel',
248    'water',
249    'waterway',
250    'wetland',
251    'width',
252    'wood',
253}
254
255local non_point_columns = {
256    'access',
257    'addr:housename',
258    'addr:housenumber',
259    'addr:interpolation',
260    'admin_level',
261    'aerialway',
262    'aeroway',
263    'amenity',
264    'area',
265    'barrier',
266    'bicycle',
267    'brand',
268    'bridge',
269    'boundary',
270    'building',
271    'construction',
272    'covered',
273    'culvert',
274    'cutting',
275    'denomination',
276    'disused',
277    'embankment',
278    'foot',
279    'generator:source',
280    'harbour',
281    'highway',
282    'historic',
283    'horse',
284    'intermittent',
285    'junction',
286    'landuse',
287    'layer',
288    'leisure',
289    'lock',
290    'man_made',
291    'military',
292    'motorcar',
293    'name',
294    'natural',
295    'office',
296    'oneway',
297    'operator',
298    'place',
299    'population',
300    'power',
301    'power_source',
302    'public_transport',
303    'railway',
304    'ref',
305    'religion',
306    'route',
307    'service',
308    'shop',
309    'sport',
310    'surface',
311    'toll',
312    'tourism',
313    'tower:type',
314    'tracktype',
315    'tunnel',
316    'water',
317    'waterway',
318    'wetland',
319    'width',
320    'wood',
321}
322
323function gen_columns(text_columns, with_hstore, area, geometry_type)
324    columns = {}
325
326    local add_column = function (name, type)
327        columns[#columns + 1] = { column = name, type = type }
328    end
329
330    for _, c in ipairs(text_columns) do
331        add_column(c, 'text')
332    end
333
334    add_column('z_order', 'int')
335
336    if area ~= nil then
337        if area then
338            add_column('way_area', 'area')
339        else
340            add_column('way_area', 'real')
341        end
342    end
343
344    if hstore_column then
345        add_column(hstore_column, 'hstore')
346    end
347
348    if with_hstore then
349        add_column('tags', 'hstore')
350    end
351
352    add_column('way', geometry_type)
353    columns[#columns].projection = srid
354
355    return columns
356end
357
358local tables = {}
359
360tables.point = osm2pgsql.define_table{
361    name = prefix .. '_point',
362    ids = { type = 'node', id_column = 'osm_id' },
363    columns = gen_columns(point_columns, hstore or hstore_all, nil, 'point')
364}
365
366tables.line = osm2pgsql.define_table{
367    name = prefix .. '_line',
368    ids = { type = 'way', id_column = 'osm_id' },
369    columns = gen_columns(non_point_columns, hstore or hstore_all, false, 'linestring')
370}
371
372tables.polygon = osm2pgsql.define_table{
373    name = prefix .. '_polygon',
374    ids = { type = 'area', id_column = 'osm_id' },
375    columns = gen_columns(non_point_columns, hstore or hstore_all, true, 'geometry')
376}
377
378tables.roads = osm2pgsql.define_table{
379    name = prefix .. '_roads',
380    ids = { type = 'way', id_column = 'osm_id' },
381    columns = gen_columns(non_point_columns, hstore or hstore_all, false, 'linestring')
382}
383
384local z_order_lookup = {
385    proposed = {1, false},
386    construction = {2, false},
387    steps = {10, false},
388    cycleway = {10, false},
389    bridleway = {10, false},
390    footway = {10, false},
391    path = {10, false},
392    track = {11, false},
393    service = {15, false},
394
395    tertiary_link = {24, false},
396    secondary_link = {25, true},
397    primary_link = {27, true},
398    trunk_link = {28, true},
399    motorway_link = {29, true},
400
401    raceway = {30, false},
402    pedestrian = {31, false},
403    living_street = {32, false},
404    road = {33, false},
405    unclassified = {33, false},
406    residential = {33, false},
407    tertiary = {34, false},
408    secondary = {36, true},
409    primary = {37, true},
410    trunk = {38, true},
411    motorway = {39, true}
412}
413
414function as_bool(value)
415    return value == 'yes' or value == 'true' or value == '1'
416end
417
418function get_z_order(tags)
419    local z_order = 100 * math.floor(tonumber(tags.layer or '0') or 0)
420    local roads = false
421
422    local highway = tags['highway']
423    if highway then
424        local r = z_order_lookup[highway] or {0, false}
425        z_order = z_order + r[1]
426        roads = r[2]
427    end
428
429    if tags.railway then
430        z_order = z_order + 35
431        roads = true
432    end
433
434    if tags.boundary and tags.boundary == 'administrative' then
435        roads = true
436    end
437
438    if as_bool(tags.bridge) then
439        z_order = z_order + 100
440    end
441
442    if as_bool(tags.tunnel) then
443        z_order = z_order - 100
444    end
445
446    return z_order, roads
447end
448
449function make_check_in_list_func(list)
450    local h = {}
451    for _, k in ipairs(list) do
452        h[k] = true
453    end
454    return function(tags)
455        for k, _ in pairs(tags) do
456            if h[k] then
457                return true
458            end
459        end
460        return false
461    end
462end
463
464local is_polygon = make_check_in_list_func(polygon_keys)
465local clean_tags = osm2pgsql.make_clean_tags_func(delete_keys)
466
467function make_column_hash(columns)
468    local h = {}
469
470    for _, k in ipairs(columns) do
471        h[k] = true
472    end
473
474    return h
475end
476
477function make_get_output(columns, hstore_all)
478    local h = make_column_hash(columns)
479    if hstore_all then
480        return function(tags)
481            local output = {}
482            local hstore_entries = {}
483
484            for k, _ in pairs(tags) do
485                if h[k] then
486                    output[k] = tags[k]
487                end
488                hstore_entries[k] = tags[k]
489            end
490
491            return output, hstore_entries
492        end
493    else
494        return function(tags)
495            local output = {}
496            local hstore_entries = {}
497
498            for k, _ in pairs(tags) do
499                if h[k] then
500                    output[k] = tags[k]
501                else
502                    hstore_entries[k] = tags[k]
503                end
504            end
505
506            return output, hstore_entries
507        end
508    end
509end
510
511local has_generic_tag = make_check_in_list_func(generic_keys)
512
513local get_point_output = make_get_output(point_columns, hstore_all)
514local get_non_point_output = make_get_output(non_point_columns, hstore_all)
515
516function get_hstore_column(tags)
517    local len = #hstore_column
518    local h = {}
519    for k, v in pairs(tags) do
520        if k:sub(1, len) == hstore_column then
521            h[k:sub(len + 1)] = v
522        end
523    end
524
525    if next(h) then
526        return h
527    end
528    return nil
529end
530
531function osm2pgsql.process_node(object)
532    if clean_tags(object.tags) then
533        return
534    end
535
536    local output
537    local output_hstore = {}
538    if hstore or hstore_all then
539        output, output_hstore = get_point_output(object.tags)
540        if not next(output) and not next(output_hstore) then
541            return
542        end
543        if hstore_match_only and not has_generic_tag(object.tags) then
544            return
545        end
546    else
547        output = object.tags
548        if not has_generic_tag(object.tags) then
549            return
550        end
551    end
552
553    output.tags = output_hstore
554
555    if hstore_column then
556        output[hstore_column] = get_hstore_column(object.tags)
557    end
558
559    tables.point:add_row(output)
560end
561
562function osm2pgsql.process_way(object)
563    if clean_tags(object.tags) then
564        return
565    end
566
567    local add_area = false
568    if object.tags.natural == 'coastline' then
569        add_area = true
570        if not keep_coastlines then
571            object.tags.natural = nil
572        end
573    end
574
575    local output
576    local output_hstore = {}
577    if hstore or hstore_all then
578        output, output_hstore = get_non_point_output(object.tags)
579        if not next(output) and not next(output_hstore) then
580            return
581        end
582        if hstore_match_only and not has_generic_tag(object.tags) then
583            return
584        end
585        if add_area and hstore_all then
586            output_hstore.area = 'yes'
587        end
588    else
589        output = object.tags
590        if not has_generic_tag(object.tags) then
591            return
592        end
593    end
594
595    local polygon
596    local area_tag = object.tags.area
597    if area_tag == 'yes' or area_tag == '1' or area_tag == 'true' then
598        polygon = true
599    elseif area_tag == 'no' or area_tag == '0' or area_tag == 'false' then
600        polygon = false
601    else
602        polygon = is_polygon(object.tags)
603    end
604
605    if add_area then
606        output.area = 'yes'
607        polygon = true
608    end
609
610    local z_order, roads = get_z_order(object.tags)
611    output.z_order = z_order
612
613    output.tags = output_hstore
614
615    if hstore_column then
616        output[hstore_column] = get_hstore_column(object.tags)
617    end
618
619    if polygon and object.is_closed then
620        output.way = { create = 'area' }
621        tables.polygon:add_row(output)
622    else
623        output.way = { create = 'line', split_at = max_length }
624        tables.line:add_row(output)
625        if roads then
626            tables.roads:add_row(output)
627        end
628    end
629end
630
631function osm2pgsql.process_relation(object)
632    if clean_tags(object.tags) then
633        return
634    end
635
636    local type = object.tags.type
637    if (type ~= 'route') and (type ~= 'multipolygon') and (type ~= 'boundary') then
638        return
639    end
640    object.tags.type = nil
641
642    local output
643    local output_hstore = {}
644    if hstore or hstore_all then
645        output, output_hstore = get_non_point_output(object.tags)
646        if not next(output) and not next(output_hstore) then
647            return
648        end
649        if hstore_match_only and not has_generic_tag(object.tags) then
650            return
651        end
652    else
653        output = object.tags
654        if not has_generic_tag(object.tags) then
655            return
656        end
657    end
658
659    if not next(output) and not next(output_hstore) then
660        return
661    end
662
663    if enable_legacy_route_processing and (hstore or hstore_all) and type == 'route' then
664        if not object.tags.route_name then
665            output_hstore.route_name = object.tags.name
666        end
667
668        local state = object.tags.state
669        if state ~= 'alternate' and state ~= 'connection' then
670            state = 'yes'
671        end
672
673        local network = object.tags.network
674        if network == 'lcn' then
675            output_hstore.lcn = output_hstore.lcn or state
676            output_hstore.lcn_ref = output_hstore.lcn_ref or object.tags.ref
677        elseif network == 'rcn' then
678            output_hstore.rcn = output_hstore.rcn or state
679            output_hstore.rcn_ref = output_hstore.rcn_ref or object.tags.ref
680        elseif network == 'ncn' then
681            output_hstore.ncn = output_hstore.ncn or state
682            output_hstore.ncn_ref = output_hstore.ncn_ref or object.tags.ref
683        elseif network == 'lwn' then
684            output_hstore.lwn = output_hstore.lwn or state
685            output_hstore.lwn_ref = output_hstore.lwn_ref or object.tags.ref
686        elseif network == 'rwn' then
687            output_hstore.rwn = output_hstore.rwn or state
688            output_hstore.rwn_ref = output_hstore.rwn_ref or object.tags.ref
689        elseif network == 'nwn' then
690            output_hstore.nwn = output_hstore.nwn or state
691            output_hstore.nwn_ref = output_hstore.nwn_ref or object.tags.ref
692        end
693
694        local pc = object.tags.preferred_color
695        if pc == '0' or pc == '1' or pc == '2' or pc == '3' or pc == '4' then
696            output_hstore.route_pref_color = pc
697        else
698            output_hstore.route_pref_color = '0'
699        end
700    end
701
702    local make_boundary = false
703    local make_polygon = false
704    if type == 'boundary' then
705        make_boundary = true
706    elseif type == 'multipolygon' and object.tags.boundary then
707        make_boundary = true
708    elseif type == 'multipolygon' then
709        make_polygon = true
710    end
711
712    local z_order, roads = get_z_order(object.tags)
713    output.z_order = z_order
714
715    output.tags = output_hstore
716
717    if hstore_column then
718        output[hstore_column] = get_hstore_column(object.tags)
719    end
720
721    if not make_polygon then
722        output.way = { create = 'line', split_at = max_length }
723        tables.line:add_row(output)
724        if roads then
725            tables.roads:add_row(output)
726        end
727    end
728
729    if make_boundary or make_polygon then
730        output.way = { create = 'area' }
731        if not multi_geometry then
732            output.way.split_at = 'multi'
733        end
734        tables.polygon:add_row(output)
735    end
736end
737
738