overture_maps 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +29 -0
  3. data/LICENSE.txt +21 -0
  4. data/README.md +416 -0
  5. data/app/controllers/overture_maps/application_controller.rb +28 -0
  6. data/app/controllers/overture_maps/attribution_controller.rb +15 -0
  7. data/app/controllers/overture_maps/features_controller.rb +125 -0
  8. data/app/controllers/overture_maps/search_controller.rb +31 -0
  9. data/app/controllers/overture_maps/tiles_controller.rb +85 -0
  10. data/config/routes.rb +13 -0
  11. data/exe/overture-maps-mcp +6 -0
  12. data/lib/generators/overture_maps/address_generator.rb +19 -0
  13. data/lib/generators/overture_maps/base_features_generator.rb +19 -0
  14. data/lib/generators/overture_maps/base_generator.rb +22 -0
  15. data/lib/generators/overture_maps/building_generator.rb +19 -0
  16. data/lib/generators/overture_maps/division_generator.rb +19 -0
  17. data/lib/generators/overture_maps/install_generator.rb +42 -0
  18. data/lib/generators/overture_maps/place_generator.rb +19 -0
  19. data/lib/generators/overture_maps/transportation_generator.rb +20 -0
  20. data/lib/generators/templates/address_migration.rb.tt +25 -0
  21. data/lib/generators/templates/address_model.rb.tt +4 -0
  22. data/lib/generators/templates/base_feature_model.rb.tt +4 -0
  23. data/lib/generators/templates/base_features_migration.rb.tt +27 -0
  24. data/lib/generators/templates/building_migration.rb.tt +24 -0
  25. data/lib/generators/templates/building_model.rb.tt +4 -0
  26. data/lib/generators/templates/category_migration.rb.tt +15 -0
  27. data/lib/generators/templates/category_model.rb.tt +4 -0
  28. data/lib/generators/templates/connector_model.rb.tt +4 -0
  29. data/lib/generators/templates/division_migration.rb.tt +31 -0
  30. data/lib/generators/templates/division_model.rb.tt +4 -0
  31. data/lib/generators/templates/imported_areas_migration.rb.tt +20 -0
  32. data/lib/generators/templates/place_migration.rb.tt +30 -0
  33. data/lib/generators/templates/place_model.rb.tt +4 -0
  34. data/lib/generators/templates/postgis_migration.rb.tt +5 -0
  35. data/lib/generators/templates/segment_model.rb.tt +4 -0
  36. data/lib/generators/templates/transportation_migration.rb.tt +38 -0
  37. data/lib/overture_maps/attribution.rb +66 -0
  38. data/lib/overture_maps/bounding_box.rb +80 -0
  39. data/lib/overture_maps/changelog.rb +70 -0
  40. data/lib/overture_maps/configuration.rb +43 -0
  41. data/lib/overture_maps/database.rb +74 -0
  42. data/lib/overture_maps/division_search.rb +44 -0
  43. data/lib/overture_maps/engine.rb +32 -0
  44. data/lib/overture_maps/geometry_parser.rb +41 -0
  45. data/lib/overture_maps/gers.rb +30 -0
  46. data/lib/overture_maps/import/downloader.rb +248 -0
  47. data/lib/overture_maps/import/location_based_runner.rb +176 -0
  48. data/lib/overture_maps/import/parquet_reader.rb +32 -0
  49. data/lib/overture_maps/import/record_mapper.rb +182 -0
  50. data/lib/overture_maps/import/runner.rb +125 -0
  51. data/lib/overture_maps/import.rb +27 -0
  52. data/lib/overture_maps/mcp_server.rb +222 -0
  53. data/lib/overture_maps/models/address.rb +32 -0
  54. data/lib/overture_maps/models/base.rb +39 -0
  55. data/lib/overture_maps/models/base_feature.rb +31 -0
  56. data/lib/overture_maps/models/building.rb +35 -0
  57. data/lib/overture_maps/models/category.rb +32 -0
  58. data/lib/overture_maps/models/connector.rb +11 -0
  59. data/lib/overture_maps/models/division.rb +43 -0
  60. data/lib/overture_maps/models/imported_area.rb +42 -0
  61. data/lib/overture_maps/models/place.rb +43 -0
  62. data/lib/overture_maps/models/segment.rb +22 -0
  63. data/lib/overture_maps/models.rb +25 -0
  64. data/lib/overture_maps/query.rb +168 -0
  65. data/lib/overture_maps/query_engine.rb +268 -0
  66. data/lib/overture_maps/releases.rb +74 -0
  67. data/lib/overture_maps/storage.rb +121 -0
  68. data/lib/overture_maps/syncer.rb +99 -0
  69. data/lib/overture_maps/util.rb +30 -0
  70. data/lib/overture_maps/version.rb +3 -0
  71. data/lib/overture_maps.rb +42 -0
  72. data/lib/tasks/overture_maps.rake +554 -0
  73. metadata +301 -0
@@ -0,0 +1,85 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OvertureMaps
4
+ # Mapbox Vector Tiles straight from PostGIS:
5
+ #
6
+ # GET /overture/tiles/places/14/2620/5723.mvt
7
+ #
8
+ # MapLibre/Leaflet can render imported data with no separate tile server:
9
+ # map.addSource("places", { type: "vector",
10
+ # tiles: ["https://app.example.com/overture/tiles/places/{z}/{x}/{y}.mvt"] })
11
+ class TilesController < ApplicationController
12
+ LAYERS = FeaturesController::RESOURCES
13
+
14
+ PROPERTY_COLUMNS = {
15
+ "places" => %w[name primary_category confidence country],
16
+ "buildings" => %w[name subtype building_class height num_floors],
17
+ "addresses" => %w[number street locality postcode],
18
+ "divisions" => %w[name subtype country],
19
+ "segments" => %w[name subtype segment_class],
20
+ "connectors" => %w[],
21
+ "base_features" => %w[name feature_type subtype feature_class]
22
+ }.freeze
23
+
24
+ MAX_ZOOM = 22
25
+ TILE_EXTENT = 4096
26
+ TILE_BUFFER = 64
27
+ # Half a buffer's worth of envelope margin so buffered geometries at the
28
+ # tile edge are included.
29
+ ENVELOPE_MARGIN = TILE_BUFFER.to_f / TILE_EXTENT
30
+
31
+ def show
32
+ layer = params[:layer]
33
+ model = LAYERS[layer]
34
+ return render json: { error: "unknown layer" }, status: :not_found unless model
35
+
36
+ z, x, y = tile_coordinates
37
+ tile = build_tile(model, layer, z, x, y)
38
+
39
+ expires_in 1.hour, public: true
40
+ send_data tile, type: "application/vnd.mapbox-vector-tile", disposition: "inline"
41
+ end
42
+
43
+ private
44
+
45
+ def tile_coordinates
46
+ z = Integer(params[:z])
47
+ x = Integer(params[:x])
48
+ y = Integer(params[:y])
49
+ max_index = (2**z) - 1
50
+ unless z.between?(0, MAX_ZOOM) && x.between?(0, max_index) && y.between?(0, max_index)
51
+ raise ArgumentError, "tile coordinates out of range"
52
+ end
53
+
54
+ [z, x, y]
55
+ end
56
+
57
+ def build_tile(model, layer, z, x, y)
58
+ connection = model.connection
59
+ columns = (PROPERTY_COLUMNS.fetch(layer, []) & model.column_names)
60
+ .map { |c| ", #{connection.quote_column_name(c)}" }.join
61
+
62
+ sql = ActiveRecord::Base.sanitize_sql(
63
+ [<<~SQL, { z: z, x: x, y: y, margin: ENVELOPE_MARGIN, limit: OvertureMaps.configuration.tile_feature_limit }]
64
+ WITH mvtgeom AS (
65
+ SELECT ST_AsMVTGeom(
66
+ ST_Transform(geometry::geometry, 3857),
67
+ ST_TileEnvelope(:z, :x, :y),
68
+ #{TILE_EXTENT}, #{TILE_BUFFER}, true
69
+ ) AS geom,
70
+ id#{columns}
71
+ FROM #{model.quoted_table_name}
72
+ WHERE geometry && ST_Transform(ST_TileEnvelope(:z, :x, :y, margin => :margin), 4326)::geography
73
+ LIMIT :limit
74
+ )
75
+ SELECT ST_AsMVT(mvtgeom.*, #{connection.quote(layer)}, #{TILE_EXTENT}, 'geom')
76
+ FROM mvtgeom WHERE geom IS NOT NULL
77
+ SQL
78
+ )
79
+
80
+ value = connection.select_value(sql) || ""
81
+ # Raw query results may arrive as hex-escaped bytea.
82
+ value.start_with?("\\x") ? [value.delete_prefix("\\x")].pack("H*") : value
83
+ end
84
+ end
85
+ end
data/config/routes.rb ADDED
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ OvertureMaps::Engine.routes.draw do
4
+ resources_constraint = { resource: /places|buildings|addresses|divisions|segments|connectors|base_features/ }
5
+
6
+ get "search", to: "search#index"
7
+ get "attribution", to: "attribution#index"
8
+ get "tiles/:layer/:z/:x/:y", to: "tiles#show",
9
+ constraints: { z: /\d+/, x: /\d+/, y: /\d+/ }
10
+
11
+ get ":resource", to: "features#index", constraints: resources_constraint
12
+ get ":resource/:id", to: "features#show", constraints: resources_constraint
13
+ end
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "overture_maps/mcp_server"
5
+
6
+ OvertureMaps::MCPServer.start
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "generators/overture_maps/base_generator"
4
+
5
+ module OvertureMaps
6
+ module Generators
7
+ class AddressGenerator < BaseGenerator
8
+ desc "Creates the overture_addresses table and model"
9
+
10
+ def create_migration
11
+ migration_template "address_migration.rb.tt", "db/migrate/create_overture_addresses.rb"
12
+ end
13
+
14
+ def create_model
15
+ template "address_model.rb.tt", "app/models/overture_address.rb"
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "generators/overture_maps/base_generator"
4
+
5
+ module OvertureMaps
6
+ module Generators
7
+ class BaseFeaturesGenerator < BaseGenerator
8
+ desc "Creates the overture_base_features table and model"
9
+
10
+ def create_migration
11
+ migration_template "base_features_migration.rb.tt", "db/migrate/create_overture_base_features.rb"
12
+ end
13
+
14
+ def create_model
15
+ template "base_feature_model.rb.tt", "app/models/overture_base_feature.rb"
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails/generators"
4
+ require "rails/generators/active_record"
5
+
6
+ module OvertureMaps
7
+ module Generators
8
+ class BaseGenerator < Rails::Generators::Base
9
+ include ActiveRecord::Generators::Migration
10
+
11
+ def self.default_source_root
12
+ File.expand_path("../templates", __dir__)
13
+ end
14
+
15
+ private
16
+
17
+ def migration_version
18
+ "[#{ActiveRecord::VERSION::STRING.to_f}]"
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "generators/overture_maps/base_generator"
4
+
5
+ module OvertureMaps
6
+ module Generators
7
+ class BuildingGenerator < BaseGenerator
8
+ desc "Creates the overture_buildings table and model"
9
+
10
+ def create_migration
11
+ migration_template "building_migration.rb.tt", "db/migrate/create_overture_buildings.rb"
12
+ end
13
+
14
+ def create_model
15
+ template "building_model.rb.tt", "app/models/overture_building.rb"
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "generators/overture_maps/base_generator"
4
+
5
+ module OvertureMaps
6
+ module Generators
7
+ class DivisionGenerator < BaseGenerator
8
+ desc "Creates the overture_divisions table and model"
9
+
10
+ def create_migration
11
+ migration_template "division_migration.rb.tt", "db/migrate/create_overture_divisions.rb"
12
+ end
13
+
14
+ def create_model
15
+ template "division_model.rb.tt", "app/models/overture_division.rb"
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "generators/overture_maps/base_generator"
4
+
5
+ module OvertureMaps
6
+ module Generators
7
+ class InstallGenerator < BaseGenerator
8
+ desc "Creates the PostGIS extension, Overture tables, and model files"
9
+
10
+ def create_migrations
11
+ migration_template "postgis_migration.rb.tt", "db/migrate/create_overture_postgis.rb"
12
+ migration_template "category_migration.rb.tt", "db/migrate/create_overture_categories.rb"
13
+ migration_template "place_migration.rb.tt", "db/migrate/create_overture_places.rb"
14
+ migration_template "building_migration.rb.tt", "db/migrate/create_overture_buildings.rb"
15
+ migration_template "address_migration.rb.tt", "db/migrate/create_overture_addresses.rb"
16
+ migration_template "division_migration.rb.tt", "db/migrate/create_overture_divisions.rb"
17
+ migration_template "transportation_migration.rb.tt", "db/migrate/create_overture_transportation.rb"
18
+ migration_template "base_features_migration.rb.tt", "db/migrate/create_overture_base_features.rb"
19
+ migration_template "imported_areas_migration.rb.tt", "db/migrate/create_overture_imported_areas.rb"
20
+ end
21
+
22
+ def create_models
23
+ template "category_model.rb.tt", "app/models/overture_category.rb"
24
+ template "place_model.rb.tt", "app/models/overture_place.rb"
25
+ template "building_model.rb.tt", "app/models/overture_building.rb"
26
+ template "address_model.rb.tt", "app/models/overture_address.rb"
27
+ template "division_model.rb.tt", "app/models/overture_division.rb"
28
+ template "segment_model.rb.tt", "app/models/overture_segment.rb"
29
+ template "connector_model.rb.tt", "app/models/overture_connector.rb"
30
+ template "base_feature_model.rb.tt", "app/models/overture_base_feature.rb"
31
+ end
32
+
33
+ def add_engine_route
34
+ route <<~ROUTE.strip
35
+ # Overture Maps read-only API (JSON/GeoJSON + MVT tiles).
36
+ # Guard with config.api_auth in an initializer if the app is public.
37
+ mount OvertureMaps::Engine => "/overture"
38
+ ROUTE
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "generators/overture_maps/base_generator"
4
+
5
+ module OvertureMaps
6
+ module Generators
7
+ class PlaceGenerator < BaseGenerator
8
+ desc "Creates the overture_places table and model"
9
+
10
+ def create_migration
11
+ migration_template "place_migration.rb.tt", "db/migrate/create_overture_places.rb"
12
+ end
13
+
14
+ def create_model
15
+ template "place_model.rb.tt", "app/models/overture_place.rb"
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "generators/overture_maps/base_generator"
4
+
5
+ module OvertureMaps
6
+ module Generators
7
+ class TransportationGenerator < BaseGenerator
8
+ desc "Creates the overture_segments/overture_connectors tables and models"
9
+
10
+ def create_migration
11
+ migration_template "transportation_migration.rb.tt", "db/migrate/create_overture_transportation.rb"
12
+ end
13
+
14
+ def create_models
15
+ template "segment_model.rb.tt", "app/models/overture_segment.rb"
16
+ template "connector_model.rb.tt", "app/models/overture_connector.rb"
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,25 @@
1
+ class CreateOvertureAddresses < ActiveRecord::Migration<%= migration_version %>
2
+ def change
3
+ create_table :overture_addresses, id: :string do |t|
4
+ t.string :number
5
+ t.string :street
6
+ t.string :unit
7
+ t.string :locality
8
+ t.string :region
9
+ t.string :postcode
10
+ t.string :country
11
+ t.string :postal_city
12
+ t.jsonb :address_levels, default: []
13
+ t.jsonb :sources, default: []
14
+ t.string :overture_release
15
+ t.timestamps
16
+ end
17
+
18
+ add_column :overture_addresses, :geometry, :st_point, geographic: true, srid: 4326
19
+ add_index :overture_addresses, :geometry, using: :gist
20
+
21
+ add_index :overture_addresses, :country
22
+ add_index :overture_addresses, :locality
23
+ add_index :overture_addresses, :postcode
24
+ end
25
+ end
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ class OvertureAddress < OvertureMaps::Models::Address
4
+ end
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ class OvertureBaseFeature < OvertureMaps::Models::BaseFeature
4
+ end
@@ -0,0 +1,27 @@
1
+ class CreateOvertureBaseFeatures < ActiveRecord::Migration<%= migration_version %>
2
+ def change
3
+ create_table :overture_base_features, id: :string do |t|
4
+ t.string :feature_type, null: false
5
+ t.string :name
6
+ t.jsonb :names, default: {}
7
+ t.string :subtype
8
+ t.string :feature_class
9
+ t.string :surface
10
+ t.float :elevation
11
+ t.integer :level
12
+ t.string :wikidata
13
+ t.jsonb :source_tags, default: {}
14
+ t.jsonb :sources, default: []
15
+ t.string :overture_release
16
+ t.timestamps
17
+ end
18
+
19
+ # Base features span points, linestrings, and polygons.
20
+ add_column :overture_base_features, :geometry, :geometry, geographic: true, srid: 4326
21
+ add_index :overture_base_features, :geometry, using: :gist
22
+
23
+ add_index :overture_base_features, :feature_type
24
+ add_index :overture_base_features, :subtype
25
+ add_index :overture_base_features, :feature_class
26
+ end
27
+ end
@@ -0,0 +1,24 @@
1
+ class CreateOvertureBuildings < ActiveRecord::Migration<%= migration_version %>
2
+ def change
3
+ create_table :overture_buildings, id: :string do |t|
4
+ t.string :name
5
+ t.jsonb :names, default: {}
6
+ t.string :subtype
7
+ t.string :building_class
8
+ t.float :height
9
+ t.integer :num_floors
10
+ t.integer :level
11
+ t.boolean :is_underground, default: false
12
+ t.jsonb :sources, default: []
13
+ t.string :overture_release
14
+ t.timestamps
15
+ end
16
+
17
+ # Generic geography: building footprints are polygons/multipolygons.
18
+ add_column :overture_buildings, :geometry, :geometry, geographic: true, srid: 4326
19
+ add_index :overture_buildings, :geometry, using: :gist
20
+
21
+ add_index :overture_buildings, :height
22
+ add_index :overture_buildings, :building_class
23
+ end
24
+ end
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ class OvertureBuilding < OvertureMaps::Models::Building
4
+ end
@@ -0,0 +1,15 @@
1
+ class CreateOvertureCategories < ActiveRecord::Migration<%= migration_version %>
2
+ def change
3
+ create_table :overture_categories do |t|
4
+ t.string :name, null: false
5
+ t.string :primary_category
6
+ t.integer :hierarchy_level, default: 0
7
+ t.jsonb :taxonomy, default: []
8
+
9
+ t.timestamps
10
+ end
11
+
12
+ add_index :overture_categories, :name, unique: true
13
+ add_index :overture_categories, :primary_category
14
+ end
15
+ end
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ class OvertureCategory < OvertureMaps::Models::Category
4
+ end
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ class OvertureConnector < OvertureMaps::Models::Connector
4
+ end
@@ -0,0 +1,31 @@
1
+ class CreateOvertureDivisions < ActiveRecord::Migration<%= migration_version %>
2
+ def change
3
+ create_table :overture_divisions, id: :string do |t|
4
+ t.string :division_id
5
+ t.string :name
6
+ t.jsonb :names, default: {}
7
+ t.string :subtype
8
+ t.string :division_class
9
+ t.string :country
10
+ t.string :region
11
+ t.boolean :is_land
12
+ t.boolean :is_territorial
13
+ t.float :bbox_xmin
14
+ t.float :bbox_xmax
15
+ t.float :bbox_ymin
16
+ t.float :bbox_ymax
17
+ t.jsonb :sources, default: []
18
+ t.string :overture_release
19
+ t.timestamps
20
+ end
21
+
22
+ # Division areas are polygons/multipolygons.
23
+ add_column :overture_divisions, :geometry, :geometry, geographic: true, srid: 4326
24
+ add_index :overture_divisions, :geometry, using: :gist
25
+
26
+ add_index :overture_divisions, :name
27
+ add_index :overture_divisions, :subtype
28
+ add_index :overture_divisions, :country
29
+ add_index :overture_divisions, :division_id
30
+ end
31
+ end
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ class OvertureDivision < OvertureMaps::Models::Division
4
+ end
@@ -0,0 +1,20 @@
1
+ class CreateOvertureImportedAreas < ActiveRecord::Migration<%= migration_version %>
2
+ def change
3
+ create_table :overture_imported_areas do |t|
4
+ t.string :theme, null: false
5
+ t.string :feature_type, null: false
6
+ t.string :model_class_name, null: false
7
+ t.string :slug, null: false
8
+ t.float :bbox_xmin
9
+ t.float :bbox_xmax
10
+ t.float :bbox_ymin
11
+ t.float :bbox_ymax
12
+ t.string :release, null: false
13
+ t.integer :records_count, default: 0
14
+ t.timestamps
15
+ end
16
+
17
+ add_index :overture_imported_areas, [:theme, :feature_type, :slug], unique: true
18
+ add_index :overture_imported_areas, :release
19
+ end
20
+ end
@@ -0,0 +1,30 @@
1
+ class CreateOverturePlaces < ActiveRecord::Migration<%= migration_version %>
2
+ def change
3
+ create_table :overture_places, id: :string do |t|
4
+ t.string :name
5
+ t.jsonb :names, default: {}
6
+ t.jsonb :categories, default: {}
7
+ t.string :primary_category
8
+ t.jsonb :brands, default: {}
9
+ t.jsonb :addresses, default: []
10
+ t.jsonb :sources, default: []
11
+ t.jsonb :websites, default: []
12
+ t.jsonb :socials, default: []
13
+ t.jsonb :emails, default: []
14
+ t.jsonb :phones, default: []
15
+ t.decimal :confidence, precision: 3, scale: 2
16
+ t.string :operating_status
17
+ t.string :country
18
+ t.string :overture_release
19
+ t.timestamps
20
+ end
21
+
22
+ add_column :overture_places, :geometry, :st_point, geographic: true, srid: 4326
23
+ add_index :overture_places, :geometry, using: :gist
24
+
25
+ add_index :overture_places, :name
26
+ add_index :overture_places, :country
27
+ add_index :overture_places, :primary_category
28
+ add_index :overture_places, :categories, using: :gin
29
+ end
30
+ end
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ class OverturePlace < OvertureMaps::Models::Place
4
+ end
@@ -0,0 +1,5 @@
1
+ class CreateOverturePostgis < ActiveRecord::Migration<%= migration_version %>
2
+ def change
3
+ enable_extension "postgis"
4
+ end
5
+ end
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ class OvertureSegment < OvertureMaps::Models::Segment
4
+ end
@@ -0,0 +1,38 @@
1
+ class CreateOvertureTransportation < ActiveRecord::Migration<%= migration_version %>
2
+ def change
3
+ create_table :overture_segments, id: :string do |t|
4
+ t.string :name
5
+ t.jsonb :names, default: {}
6
+ t.string :subtype
7
+ t.string :segment_class
8
+ t.string :subclass
9
+ t.jsonb :connectors, default: []
10
+ t.jsonb :routes, default: []
11
+ t.jsonb :speed_limits, default: []
12
+ t.jsonb :access_restrictions, default: []
13
+ t.jsonb :prohibited_transitions, default: []
14
+ t.jsonb :road_surface, default: []
15
+ t.jsonb :road_flags, default: []
16
+ t.jsonb :destinations, default: []
17
+ t.jsonb :sources, default: []
18
+ t.string :overture_release
19
+ t.timestamps
20
+ end
21
+
22
+ # Segments are linestrings.
23
+ add_column :overture_segments, :geometry, :geometry, geographic: true, srid: 4326
24
+ add_index :overture_segments, :geometry, using: :gist
25
+
26
+ add_index :overture_segments, :subtype
27
+ add_index :overture_segments, :segment_class
28
+
29
+ create_table :overture_connectors, id: :string do |t|
30
+ t.jsonb :sources, default: []
31
+ t.string :overture_release
32
+ t.timestamps
33
+ end
34
+
35
+ add_column :overture_connectors, :geometry, :st_point, geographic: true, srid: 4326
36
+ add_index :overture_connectors, :geometry, using: :gist
37
+ end
38
+ end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OvertureMaps
4
+ # Builds license-correct attribution from the data actually imported. The
5
+ # `sources` column preserved on every row records which upstream datasets
6
+ # contributed, which is what determines the required notices:
7
+ #
8
+ # OvertureMaps::Attribution.notices # => array of notice strings
9
+ # OvertureMaps::Attribution.text # => single line for a map corner
10
+ #
11
+ # Overture licensing: ODbL for OSM-derived themes (base, buildings,
12
+ # divisions, transportation), CDLA-Permissive-2.0 for places (Foursquare
13
+ # requires referencing their NOTICE), per-source terms for addresses.
14
+ # See https://docs.overturemaps.org/attribution/
15
+ module Attribution
16
+ FOURSQUARE_NOTICE_URL = "https://opensource.foursquare.com/places-notice-txt/"
17
+
18
+ class << self
19
+ def notices(models: default_models)
20
+ datasets = dataset_names(models: models)
21
+
22
+ notices = ["Overture Maps Foundation — overturemaps.org"]
23
+ notices << "© OpenStreetMap contributors (ODbL)" if datasets.any? { |d| d.match?(/openstreetmap|\bosm\b/i) }
24
+ if datasets.any? { |d| d.match?(/foursquare/i) }
25
+ notices << "Includes Foursquare data (CDLA-Permissive-2.0) — see #{FOURSQUARE_NOTICE_URL}"
26
+ end
27
+
28
+ remaining = datasets.reject { |d| d.match?(/openstreetmap|\bosm\b|foursquare/i) }
29
+ notices << "Data sources: #{remaining.sort.join(", ")}" if remaining.any?
30
+ notices
31
+ end
32
+
33
+ def text(models: default_models)
34
+ notices(models: models).join(" · ")
35
+ end
36
+
37
+ # Distinct upstream dataset names across the given models' sources
38
+ # columns.
39
+ def dataset_names(models: default_models)
40
+ models.flat_map { |model| datasets_for(model) }.uniq
41
+ end
42
+
43
+ def datasets_for(model)
44
+ return [] unless model.table_exists? && model.column_names.include?("sources")
45
+
46
+ model.connection.select_values(<<~SQL).compact
47
+ SELECT DISTINCT elem->>'dataset'
48
+ FROM #{model.quoted_table_name},
49
+ LATERAL jsonb_array_elements(sources) AS elem
50
+ WHERE sources IS NOT NULL AND jsonb_typeof(sources) = 'array'
51
+ SQL
52
+ rescue StandardError
53
+ []
54
+ end
55
+
56
+ private
57
+
58
+ def default_models
59
+ [
60
+ Models::Place, Models::Building, Models::Address, Models::Division,
61
+ Models::Segment, Models::Connector, Models::BaseFeature
62
+ ]
63
+ end
64
+ end
65
+ end
66
+ end