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,99 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OvertureMaps
4
+ # Brings imported areas up to a newer Overture release without full
5
+ # reloads. For each tracked area (see Models::ImportedArea):
6
+ #
7
+ # 1. When every intermediate release's changelog is known, delete the ids
8
+ # marked `removed` in each step (bbox-scoped), then re-extract and
9
+ # upsert the area at the target release — upserts cover `added` and
10
+ # `data_changed`, deletions cover `removed`.
11
+ # 2. When the chain is incomplete (the area's release is no longer in the
12
+ # catalog), fall back to a full refresh: delete the area's rows and
13
+ # re-import.
14
+ #
15
+ # Overlapping areas sharing a table converge once all areas are synced to
16
+ # the same target.
17
+ class Syncer
18
+ Result = Struct.new(:area, :status, :removed, :imported, :errors, :message, keyword_init: true)
19
+
20
+ DELETE_BATCH_SIZE = 1000
21
+
22
+ def initialize(target_release: nil)
23
+ @target = Releases.validate!(target_release || Releases.current)
24
+ end
25
+
26
+ attr_reader :target
27
+
28
+ def sync_all
29
+ Models::ImportedArea.find_each.map { |area| sync_area(area) }
30
+ end
31
+
32
+ def sync_area(area)
33
+ return Result.new(area: area, status: :up_to_date, removed: 0, imported: 0, errors: 0) if area.release == target
34
+
35
+ model = area.model_class
36
+ bbox = area.to_bounding_box
37
+ steps = releases_between(area.release, target)
38
+
39
+ if steps
40
+ removed = delete_removed(area, model, bbox, steps)
41
+ runner = reimport(area, model, bbox)
42
+ finalize(area, runner)
43
+ Result.new(area: area, status: :synced, removed: removed,
44
+ imported: runner.imported_count, errors: runner.error_count)
45
+ else
46
+ purged = purge_area(model, bbox)
47
+ runner = reimport(area, model, bbox)
48
+ finalize(area, runner)
49
+ Result.new(area: area, status: :refreshed, removed: purged,
50
+ imported: runner.imported_count, errors: runner.error_count,
51
+ message: "changelog chain from #{area.release} unavailable; did a full refresh")
52
+ end
53
+ rescue StandardError => e
54
+ Result.new(area: area, status: :failed, removed: 0, imported: 0, errors: 1, message: e.message)
55
+ end
56
+
57
+ # The releases after `from` up to and including `to`, oldest first, or
58
+ # nil when the chain can't be established from the known release list.
59
+ def releases_between(from, to)
60
+ known = Releases.all.sort
61
+ from_idx = known.index(from)
62
+ to_idx = known.index(to)
63
+ return nil if from_idx.nil? || to_idx.nil? || to_idx <= from_idx
64
+
65
+ known[(from_idx + 1)..to_idx]
66
+ end
67
+
68
+ private
69
+
70
+ def delete_removed(area, model, bbox, steps)
71
+ total = 0
72
+ steps.each do |release|
73
+ ids = Changelog.removed_ids(theme: area.theme, type: area.feature_type,
74
+ release: release, bbox: bbox)
75
+ ids.each_slice(DELETE_BATCH_SIZE) do |slice|
76
+ total += model.where(id: slice).delete_all
77
+ end
78
+ end
79
+ total
80
+ end
81
+
82
+ def purge_area(model, bbox)
83
+ model.within_bounds(bbox.min_lat, bbox.min_lng, bbox.max_lat, bbox.max_lng).delete_all
84
+ end
85
+
86
+ def reimport(area, model, bbox)
87
+ Import::LocationBasedRunner.new(
88
+ theme: area.theme,
89
+ location: bbox,
90
+ models: { area.feature_type => model },
91
+ release: target
92
+ ).run
93
+ end
94
+
95
+ def finalize(area, runner)
96
+ area.update!(release: target, records_count: runner.imported_count)
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OvertureMaps
4
+ module Util
5
+ module_function
6
+
7
+ def format_size(bytes)
8
+ if bytes >= 1_073_741_824
9
+ "#{(bytes / 1_073_741_824.0).round(2)} GB"
10
+ elsif bytes >= 1_048_576
11
+ "#{(bytes / 1_048_576.0).round(2)} MB"
12
+ elsif bytes >= 1024
13
+ "#{(bytes / 1024.0).round(2)} KB"
14
+ else
15
+ "#{bytes} bytes"
16
+ end
17
+ end
18
+
19
+ # Approximate area of a bounding box in km².
20
+ def bbox_area_km2(bbox)
21
+ lat_center = (bbox.min_lat + bbox.max_lat) / 2.0
22
+ km_per_deg_lat = 111.0
23
+ km_per_deg_lng = 111.0 * Math.cos(lat_center * Math::PI / 180.0)
24
+
25
+ width_km = (bbox.max_lng - bbox.min_lng) * km_per_deg_lng
26
+ height_km = (bbox.max_lat - bbox.min_lat) * km_per_deg_lat
27
+ (width_km * height_km).abs.round(2)
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,3 @@
1
+ module OvertureMaps
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OvertureMaps
4
+ class Error < StandardError; end
5
+ class ConfigurationError < Error; end
6
+ class CancelledError < Error; end
7
+
8
+ class << self
9
+ def configure
10
+ yield(configuration)
11
+ end
12
+
13
+ def configuration
14
+ @configuration ||= Configuration.new
15
+ end
16
+
17
+ def reset
18
+ @configuration = nil
19
+ Releases.reset!
20
+ QueryEngine.reset!
21
+ end
22
+ end
23
+ end
24
+
25
+ require "overture_maps/version"
26
+ require "overture_maps/configuration"
27
+ require "overture_maps/util"
28
+ require "overture_maps/bounding_box"
29
+ require "overture_maps/storage"
30
+ require "overture_maps/releases"
31
+ require "overture_maps/query_engine"
32
+ require "overture_maps/database"
33
+ require "overture_maps/geometry_parser"
34
+ require "overture_maps/models"
35
+ require "overture_maps/division_search"
36
+ require "overture_maps/import"
37
+ require "overture_maps/query"
38
+ require "overture_maps/gers"
39
+ require "overture_maps/changelog"
40
+ require "overture_maps/syncer"
41
+ require "overture_maps/attribution"
42
+ require "overture_maps/engine" if defined?(Rails::Railtie)