tg_geometry 0.1.0 → 0.3.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 (91) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +55 -79
  3. data/README.md +168 -184
  4. data/Rakefile +3 -3
  5. data/benchmark/ewkb_roundtrip.rb +29 -0
  6. data/benchmark/falcon_concurrency.rb +1 -1
  7. data/benchmark/feature_source.rb +92 -0
  8. data/benchmark/geom_query.rb +33 -0
  9. data/benchmark/nearest_segment.rb +20 -0
  10. data/docs/ARCHITECTURE.md +29 -107
  11. data/docs/BENCHMARKING.md +20 -1
  12. data/docs/CASUAL_EXAMPLE.md +71 -458
  13. data/docs/CONCURRENCY.md +13 -7
  14. data/docs/ERROR_HANDLING.md +30 -0
  15. data/docs/FEATURE_SOURCE.md +166 -0
  16. data/docs/GEOMETRY_QUERIES.md +23 -0
  17. data/docs/LIMITATIONS.md +17 -54
  18. data/docs/MEMORY_OWNERSHIP.md +20 -2
  19. data/docs/NEAREST_SEGMENT.md +17 -0
  20. data/docs/SRID_AND_EWKB.md +23 -0
  21. data/ext/tg_geometry/extconf.rb +46 -4
  22. data/ext/tg_geometry/tg_geometry_ext.c +3643 -168
  23. data/ext/tg_geometry/tg_geometry_vendor_json.c +17 -0
  24. data/ext/tg_geometry/tg_geometry_vendor_tg.c +3 -0
  25. data/ext/tg_geometry/vendor/.vendored +8 -2
  26. data/ext/tg_geometry/vendor/json/LICENSE +20 -0
  27. data/ext/tg_geometry/vendor/json/VERSION +3 -0
  28. data/ext/tg_geometry/vendor/json/json.c +1024 -0
  29. data/ext/tg_geometry/vendor/json/json.h +207 -0
  30. data/lib/tg/geometry/active_record_source.rb +16 -34
  31. data/lib/tg/geometry/active_record_type.rb +61 -0
  32. data/lib/tg/geometry/registry.rb +16 -67
  33. data/lib/tg/geometry/version.rb +1 -1
  34. data/lib/tg/geometry.rb +85 -0
  35. data/script/vendor_libs.rb +22 -6
  36. data/spec/active_record_type_spec.rb +45 -0
  37. data/spec/{expansion_a_auto_strategy_spec.rb → auto_strategy_spec.rb} +1 -1
  38. data/spec/{block_12_batch_packed_spec.rb → batch_packed_spec.rb} +1 -1
  39. data/spec/{block_20_concurrency_spec.rb → concurrency_spec.rb} +1 -1
  40. data/spec/constructors_spec.rb +104 -0
  41. data/spec/{block_13_error_hardening_spec.rb → error_hardening_spec.rb} +1 -1
  42. data/spec/feature_source_nogvl_spec.rb +51 -0
  43. data/spec/feature_source_spec.rb +268 -0
  44. data/spec/fixtures/feature_source/invalid_geometry_middle.geojson +8 -0
  45. data/spec/fixtures/feature_source/malformed_json.geojson +1 -0
  46. data/spec/fixtures/feature_source/mixed_geometry_types.geojson +8 -0
  47. data/spec/fixtures/feature_source/osm_like_feature_collection.geojson +10 -0
  48. data/spec/fixtures/feature_source/properties_null_missing.geojson +7 -0
  49. data/spec/fixtures/feature_source/simple_feature_collection.geojson +15 -0
  50. data/spec/fixtures/postgis/README.md +16 -0
  51. data/spec/fixtures/postgis/boundary_point_cases.geojson +83 -0
  52. data/spec/fixtures/postgis/multipolygon_large.ewkb +0 -0
  53. data/spec/fixtures/postgis/point_4326.ewkb +0 -0
  54. data/spec/fixtures/postgis/polygon_3857.ewkb +0 -0
  55. data/spec/fixtures/postgis/polygon_4326_simple.ewkb +0 -0
  56. data/spec/fixtures/postgis/polygon_4326_with_hole.ewkb +0 -0
  57. data/spec/{expansion_d_format_coverage_spec.rb → format_coverage_spec.rb} +1 -1
  58. data/spec/{block_20_fuzz_spec.rb → fuzz_spec.rb} +1 -1
  59. data/spec/{block_4_geom_api_spec.rb → geom_api_spec.rb} +1 -1
  60. data/spec/{block_3_geom_parse_spec.rb → geom_parse_spec.rb} +1 -1
  61. data/spec/{block_8_index_borrowed_geometry_spec.rb → index_borrowed_geometry_spec.rb} +1 -1
  62. data/spec/{block_6_index_build_spec.rb → index_build_spec.rb} +2 -2
  63. data/spec/{block_9_flat_query_spec.rb → index_flat_query_spec.rb} +1 -1
  64. data/spec/index_geom_query_spec.rb +68 -0
  65. data/spec/{block_7_index_owned_geometry_spec.rb → index_owned_geometry_spec.rb} +1 -1
  66. data/spec/{block_10_rtree_strategy_spec.rb → index_rtree_accounting_spec.rb} +1 -1
  67. data/spec/{block_11_rtree_order_spec.rb → index_rtree_order_spec.rb} +1 -1
  68. data/spec/keyword_validation_spec.rb +31 -0
  69. data/spec/{block_1_skeleton_spec.rb → load_and_errors_spec.rb} +1 -1
  70. data/spec/{expansion_e_low_level_geometry_spec.rb → low_level_geometry_spec.rb} +1 -1
  71. data/spec/{block_14_memory_gc_hardening_spec.rb → memory_gc_spec.rb} +1 -1
  72. data/spec/nearest_segment_spec.rb +62 -0
  73. data/spec/postgis_fixtures_spec.rb +68 -0
  74. data/spec/{expansion_i_ractor_spec.rb → ractor_spec.rb} +1 -1
  75. data/spec/{block_5_rect_api_spec.rb → rect_api_spec.rb} +1 -1
  76. data/spec/{expansion_b_registry_spec.rb → registry_spec.rb} +1 -1
  77. data/spec/srid_spec.rb +43 -0
  78. data/spec/{expansion_j_full_tg_api_coverage_spec.rb → tg_api_coverage_spec.rb} +1 -1
  79. data/spec/to_ewkb_spec.rb +37 -0
  80. data/spec/{block_2_vendor_spec.rb → vendor_sources_spec.rb} +4 -4
  81. metadata +87 -38
  82. data/docs/ACTIVE_RECORD.md +0 -26
  83. data/docs/AUTO_STRATEGY.md +0 -15
  84. data/docs/EXPANSION_E_TO_H_STATUS.md +0 -51
  85. data/docs/FORMAT_COVERAGE.md +0 -23
  86. data/docs/FULL_TG_API_COVERAGE.md +0 -109
  87. data/docs/LOW_LEVEL_GEOMETRY.md +0 -121
  88. data/docs/RACTOR.md +0 -40
  89. data/docs/REGISTRY.md +0 -37
  90. data/docs/RELEASE_CHECKLIST.md +0 -39
  91. /data/spec/{expansion_c_active_record_source_spec.rb → active_record_source_spec.rb} +0 -0
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tg_geometry
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Roman Haydarov
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-05-25 00:00:00.000000000 Z
11
+ date: 2026-05-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -38,14 +38,35 @@ dependencies:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
40
  version: '3.13'
41
+ - !ruby/object:Gem::Dependency
42
+ name: activemodel
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '6.0'
48
+ - - "<"
49
+ - !ruby/object:Gem::Version
50
+ version: '9.0'
51
+ type: :development
52
+ prerelease: false
53
+ version_requirements: !ruby/object:Gem::Requirement
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ version: '6.0'
58
+ - - "<"
59
+ - !ruby/object:Gem::Version
60
+ version: '9.0'
41
61
  description: Defines TG::Geometry with immutable Geom parsing and constructor wrappers,
42
62
  expanded geometry predicates and accessors, Rect helpers, Hex/GeoBIN writers, raw
43
63
  extra_json access, read-only borrowed Line/Ring/Polygon and GeometryCollection child
44
64
  wrappers, value Segment wrappers, Registry reload sugar, optional ActiveRecord source
45
65
  helpers, and an immutable geofencing-oriented Index with owned and borrowed geometry
46
66
  ingestion, flat/rtree strategies, deterministic ordered id results, exact rtree
47
- allocation accounting, and native-endian packed point batch queries over vendored
48
- TG sources. Ractor support is not claimed.
67
+ allocation accounting, and native-endian packed point batch queries, and FeatureSource
68
+ GeoJSON FeatureCollection extraction/build paths over vendored C sources. Ractor
69
+ support is not claimed.
49
70
  email:
50
71
  - romnhajdarov@gmail.com
51
72
  executables: []
@@ -60,33 +81,37 @@ files:
60
81
  - Rakefile
61
82
  - benchmark/_support.rb
62
83
  - benchmark/batch_packed_vs_loop.rb
84
+ - benchmark/ewkb_roundtrip.rb
63
85
  - benchmark/falcon_concurrency.rb
86
+ - benchmark/feature_source.rb
64
87
  - benchmark/flat_vs_rtree.rb
88
+ - benchmark/geom_query.rb
65
89
  - benchmark/gvl_threshold.rb
90
+ - benchmark/nearest_segment.rb
66
91
  - benchmark/objectspace_memsize.rb
67
92
  - benchmark/parse_throughput.rb
68
93
  - benchmark/rss_stability.rb
69
- - docs/ACTIVE_RECORD.md
70
94
  - docs/ARCHITECTURE.md
71
- - docs/AUTO_STRATEGY.md
72
95
  - docs/BENCHMARKING.md
73
96
  - docs/CASUAL_EXAMPLE.md
74
97
  - docs/CONCURRENCY.md
75
98
  - docs/ERROR_HANDLING.md
76
- - docs/EXPANSION_E_TO_H_STATUS.md
77
- - docs/FORMAT_COVERAGE.md
78
- - docs/FULL_TG_API_COVERAGE.md
99
+ - docs/FEATURE_SOURCE.md
100
+ - docs/GEOMETRY_QUERIES.md
79
101
  - docs/LIMITATIONS.md
80
- - docs/LOW_LEVEL_GEOMETRY.md
81
102
  - docs/MEMORY_OWNERSHIP.md
82
- - docs/RACTOR.md
83
- - docs/REGISTRY.md
84
- - docs/RELEASE_CHECKLIST.md
103
+ - docs/NEAREST_SEGMENT.md
104
+ - docs/SRID_AND_EWKB.md
85
105
  - ext/tg_geometry/extconf.rb
86
106
  - ext/tg_geometry/tg_geometry_ext.c
107
+ - ext/tg_geometry/tg_geometry_vendor_json.c
87
108
  - ext/tg_geometry/tg_geometry_vendor_rtree.c
88
109
  - ext/tg_geometry/tg_geometry_vendor_tg.c
89
110
  - ext/tg_geometry/vendor/.vendored
111
+ - ext/tg_geometry/vendor/json/LICENSE
112
+ - ext/tg_geometry/vendor/json/VERSION
113
+ - ext/tg_geometry/vendor/json/json.c
114
+ - ext/tg_geometry/vendor/json/json.h
90
115
  - ext/tg_geometry/vendor/rtree/LICENSE
91
116
  - ext/tg_geometry/vendor/rtree/README.md
92
117
  - ext/tg_geometry/vendor/rtree/VERSION
@@ -99,34 +124,58 @@ files:
99
124
  - ext/tg_geometry/vendor/tg/tg.h
100
125
  - lib/tg/geometry.rb
101
126
  - lib/tg/geometry/active_record_source.rb
127
+ - lib/tg/geometry/active_record_type.rb
102
128
  - lib/tg/geometry/registry.rb
103
129
  - lib/tg/geometry/version.rb
104
130
  - lib/tg_geometry.rb
105
131
  - script/vendor_libs.rb
106
- - spec/block_10_rtree_strategy_spec.rb
107
- - spec/block_11_rtree_order_spec.rb
108
- - spec/block_12_batch_packed_spec.rb
109
- - spec/block_13_error_hardening_spec.rb
110
- - spec/block_14_memory_gc_hardening_spec.rb
111
- - spec/block_1_skeleton_spec.rb
112
- - spec/block_20_concurrency_spec.rb
113
- - spec/block_20_fuzz_spec.rb
114
- - spec/block_2_vendor_spec.rb
115
- - spec/block_3_geom_parse_spec.rb
116
- - spec/block_4_geom_api_spec.rb
117
- - spec/block_5_rect_api_spec.rb
118
- - spec/block_6_index_build_spec.rb
119
- - spec/block_7_index_owned_geometry_spec.rb
120
- - spec/block_8_index_borrowed_geometry_spec.rb
121
- - spec/block_9_flat_query_spec.rb
122
- - spec/expansion_a_auto_strategy_spec.rb
123
- - spec/expansion_b_registry_spec.rb
124
- - spec/expansion_c_active_record_source_spec.rb
125
- - spec/expansion_d_format_coverage_spec.rb
126
- - spec/expansion_e_low_level_geometry_spec.rb
127
- - spec/expansion_i_ractor_spec.rb
128
- - spec/expansion_j_full_tg_api_coverage_spec.rb
132
+ - spec/active_record_source_spec.rb
133
+ - spec/active_record_type_spec.rb
134
+ - spec/auto_strategy_spec.rb
135
+ - spec/batch_packed_spec.rb
136
+ - spec/concurrency_spec.rb
137
+ - spec/constructors_spec.rb
138
+ - spec/error_hardening_spec.rb
139
+ - spec/feature_source_nogvl_spec.rb
140
+ - spec/feature_source_spec.rb
141
+ - spec/fixtures/feature_source/invalid_geometry_middle.geojson
142
+ - spec/fixtures/feature_source/malformed_json.geojson
143
+ - spec/fixtures/feature_source/mixed_geometry_types.geojson
144
+ - spec/fixtures/feature_source/osm_like_feature_collection.geojson
145
+ - spec/fixtures/feature_source/properties_null_missing.geojson
146
+ - spec/fixtures/feature_source/simple_feature_collection.geojson
147
+ - spec/fixtures/postgis/README.md
148
+ - spec/fixtures/postgis/boundary_point_cases.geojson
149
+ - spec/fixtures/postgis/multipolygon_large.ewkb
150
+ - spec/fixtures/postgis/point_4326.ewkb
151
+ - spec/fixtures/postgis/polygon_3857.ewkb
152
+ - spec/fixtures/postgis/polygon_4326_simple.ewkb
153
+ - spec/fixtures/postgis/polygon_4326_with_hole.ewkb
154
+ - spec/format_coverage_spec.rb
155
+ - spec/fuzz_spec.rb
156
+ - spec/geom_api_spec.rb
157
+ - spec/geom_parse_spec.rb
158
+ - spec/index_borrowed_geometry_spec.rb
159
+ - spec/index_build_spec.rb
160
+ - spec/index_flat_query_spec.rb
161
+ - spec/index_geom_query_spec.rb
162
+ - spec/index_owned_geometry_spec.rb
163
+ - spec/index_rtree_accounting_spec.rb
164
+ - spec/index_rtree_order_spec.rb
165
+ - spec/keyword_validation_spec.rb
166
+ - spec/load_and_errors_spec.rb
167
+ - spec/low_level_geometry_spec.rb
168
+ - spec/memory_gc_spec.rb
169
+ - spec/nearest_segment_spec.rb
170
+ - spec/postgis_fixtures_spec.rb
171
+ - spec/ractor_spec.rb
172
+ - spec/rect_api_spec.rb
173
+ - spec/registry_spec.rb
129
174
  - spec/spec_helper.rb
175
+ - spec/srid_spec.rb
176
+ - spec/tg_api_coverage_spec.rb
177
+ - spec/to_ewkb_spec.rb
178
+ - spec/vendor_sources_spec.rb
130
179
  homepage: https://github.com/roman-haidarov/tg_geometry
131
180
  licenses:
132
181
  - MIT
@@ -142,7 +191,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
142
191
  requirements:
143
192
  - - ">="
144
193
  - !ruby/object:Gem::Version
145
- version: '3.0'
194
+ version: '3.1'
146
195
  required_rubygems_version: !ruby/object:Gem::Requirement
147
196
  requirements:
148
197
  - - ">="
@@ -153,5 +202,5 @@ rubygems_version: 3.3.27
153
202
  signing_key:
154
203
  specification_version: 4
155
204
  summary: Native extension for TG::Geometry parsing, predicates, immutable indexes,
156
- registries, low-level wrappers, and packed point batches
205
+ FeatureSource imports, registries, low-level wrappers, and packed point batches
157
206
  test_files: []
@@ -1,26 +0,0 @@
1
- # ActiveRecord source helper
2
-
3
- `TG::Geometry::ActiveRecordSource` is an optional Ruby helper. It does not require Rails or ActiveRecord and is not loaded from a separate integration gem.
4
-
5
- It accepts any object that responds to `find_each`, or any Enumerable:
6
-
7
- ```ruby
8
- entries = TG::Geometry::ActiveRecordSource.call(
9
- Zone.where(active: true),
10
- id: :id,
11
- geometry: :geojson,
12
- batch_size: 1_000
13
- )
14
-
15
- index = TG::Geometry::Index.build(entries, via: :geojson, strategy: :rtree)
16
- ```
17
-
18
- It can also feed a Registry:
19
-
20
- ```ruby
21
- class DeliveryZones < TG::Geometry::Registry
22
- active_record_source Zone.where(active: true), id: :id, geometry: :geojson
23
- end
24
- ```
25
-
26
- Field readers may be Symbols, Strings, or Procs. The helper only converts application records into `[[id, object], ...]`; it does not mutate records, keep database connections, create background jobs, install reload hooks, or add a Rails dependency to the native extension.
@@ -1,15 +0,0 @@
1
- # Auto strategy status
2
-
3
- `strategy: :auto` is not exposed in the first public release.
4
-
5
- The release-core contract only enables explicit `strategy: :flat` and `strategy: :rtree`. Automatic threshold selection requires a complete project-owned benchmark matrix and explicit approval before it can become public API.
6
-
7
- Use `benchmark/flat_vs_rtree.rb` to compare strategies for a workload, then pass the chosen strategy explicitly:
8
-
9
- ```ruby
10
- index = TG::Geometry::Index.build(entries, via: :geojson, strategy: :rtree)
11
- # or
12
- index = TG::Geometry::Index.build(entries, via: :geojson, strategy: :flat)
13
- ```
14
-
15
- Do not infer a universal crossover from rtree internals or from a partial benchmark run.
@@ -1,51 +0,0 @@
1
- # Expansion Blocks E–H status
2
-
3
- ## Expansion Block E — Low-level Ring / Line / Polygon APIs
4
-
5
- Implemented as read-only borrowed wrappers:
6
-
7
- - `TG::Geometry::Geom#point`
8
- - `TG::Geometry::Geom#line`
9
- - `TG::Geometry::Geom#polygon`
10
- - `TG::Geometry::Line`
11
- - `TG::Geometry::Ring`
12
- - `TG::Geometry::Polygon`
13
-
14
- Invariant satisfied: child pointers are borrowed from the parent TG geometry and the wrapper keeps the parent Ruby `TG::Geometry::Geom` alive through `geom_owner` with compaction-aware marking.
15
-
16
- Tests: `spec/expansion_e_low_level_geometry_spec.rb` covers accessors, private allocation, out-of-range errors, and parent survival after `GC.start` / `GC.compact`.
17
-
18
- ## Expansion Block F — Callback/search APIs
19
-
20
- OPEN QUESTION: not implemented.
21
-
22
- Reason: the roadmap allows callback/search APIs only with a new callback safety contract. The current contract still forbids public callback/block APIs in the first release. No `geom.search`, `index.search`, or `each_match` method is exposed.
23
-
24
- Required before implementation:
25
-
26
- - explicit callback exception propagation semantics;
27
- - GVL behavior for Ruby callbacks inside C loops;
28
- - proof that borrowed pointers cannot be invalidated by callback reentrancy;
29
- - benchmark of callback overhead;
30
- - tests for exceptions raised from callbacks.
31
-
32
- ## Expansion Block G — Fast no-allocation point query optimization
33
-
34
- OPEN QUESTION: not implemented.
35
-
36
- Reason: the current implementation intentionally constructs a temporary TG point geometry for `covers` / `contains` correctness. Replacing this with `tg_geom_intersects_xy` or a specialized helper requires proof of exact boundary semantics and a benchmark proving benefit.
37
-
38
- Current invariant: point query semantics remain exact and boundary behavior is covered by existing tests.
39
-
40
- ## Expansion Block H — Geodesic helpers or projection integration
41
-
42
- OPEN QUESTION: not implemented.
43
-
44
- Reason: TG core remains planar XY. No optional geodesic/projection dependency has been approved. The gem continues to document that `length`, `area`, and `perimeter` are in input coordinate units, not meters for lon/lat.
45
-
46
- Required before implementation:
47
-
48
- - explicit optional dependency decision;
49
- - public API shape;
50
- - tests separating planar TG behavior from geodesic/projection helpers;
51
- - documentation that TG itself does not handle geodesics.
@@ -1,23 +0,0 @@
1
- # Format coverage
2
-
3
- Expansion Block D exposes additional TG format helpers without changing ownership rules.
4
-
5
- ## Parse helpers
6
-
7
- - `TG::Geometry.parse_hex(str, index: :ystripes)`
8
- - `TG::Geometry.parse_geobin(bytes, index: :ystripes)`
9
-
10
- These are shortcuts over `TG::Geometry.parse(..., format: :hex)` and `TG::Geometry.parse(..., format: :geobin)`.
11
-
12
- ## Writer helpers
13
-
14
- - `TG::Geometry::Geom#to_hex` -> UTF-8 String
15
- - `TG::Geometry::Geom#to_geobin` -> ASCII-8BIT String
16
-
17
- Writers use the same direct Ruby string buffer pattern as existing writers. Hex is text and GeoBIN is binary.
18
-
19
- ## Raw extra_json
20
-
21
- - `TG::Geometry::Geom#extra_json` -> UTF-8 String or nil
22
-
23
- This returns a copied Ruby String from TG's raw extra JSON pointer. It does not parse JSON into Hashes and does not expose borrowed child wrappers. This follows the constraint that raw `extra_json` is safer than implicit `JSON.parse`.
@@ -1,109 +0,0 @@
1
- # Full TG API coverage status
2
-
3
- This document covers Expansion Block J as implemented in grouped, safe increments.
4
-
5
- Block J does not mean exposing every upstream TG function in one change. The rule is: implement in groups, define ownership for each group, add tests, and avoid environment allocator overrides or global mutable settings.
6
-
7
- ## Implemented group: predicates
8
-
9
- `TG::Geometry::Geom` now exposes additional read-only predicates:
10
-
11
- - `equals?(other)`
12
- - `disjoint?(other)`
13
- - `within?(other)`
14
- - `covers?(other)`
15
- - `covered_by?(other)`
16
- - `touches?(other)`
17
- - `intersects_xy?(x, y)`
18
- - `intersects_rect?(rect)`
19
- - `intersects_rect?(min_x, min_y, max_x, max_y)`
20
-
21
- Existing methods remain:
22
-
23
- - `contains?(other)`
24
- - `intersects?(other)`
25
- - `covers_xy?(x, y)`
26
-
27
- `equals?` intentionally does not change Ruby `==`, `eql?`, or `hash`. Ruby equality remains identity-based until that open API decision is explicitly made.
28
-
29
- ## Implemented group: geometry accessors
30
-
31
- `TG::Geometry::Geom` now exposes safe read-only accessors for upstream geometry collections and coordinate metadata:
32
-
33
- - `feature?`
34
- - `feature_collection?`
35
- - `empty?`
36
- - `dims`
37
- - `has_z?`
38
- - `has_m?`
39
- - `z`
40
- - `m`
41
- - `extra_coords`
42
- - `num_points`
43
- - `point_at(index)`
44
- - `points`
45
- - `num_lines`
46
- - `line_at(index)`
47
- - `lines`
48
- - `num_polygons`
49
- - `polygon_at(index)`
50
- - `polygons`
51
- - `num_geometries`
52
- - `geometry_at(index)`
53
- - `geometries`
54
-
55
- `geometry_at` and `geometries` return borrowed immutable `TG::Geometry::Geom` wrappers. They keep the parent wrapper alive through `geom_owner`, mark it for GC, update it during compaction, and do not call `tg_geom_free` for the borrowed child pointer.
56
-
57
- ## Implemented group: point and empty constructors
58
-
59
- Safe constructors are exposed only where ownership is simple and the returned object owns exactly one `struct tg_geom *`:
60
-
61
- - `TG::Geometry.point(x, y)`
62
- - `TG::Geometry.point_z(x, y, z)`
63
- - `TG::Geometry.point_m(x, y, m)`
64
- - `TG::Geometry.point_zm(x, y, z, m)`
65
- - `TG::Geometry.empty_point`
66
- - `TG::Geometry.empty_linestring`
67
- - `TG::Geometry.empty_polygon`
68
- - `TG::Geometry.empty_multipoint`
69
- - `TG::Geometry.empty_multilinestring`
70
- - `TG::Geometry.empty_multipolygon`
71
- - `TG::Geometry.empty_geometrycollection`
72
-
73
- Numeric coordinates are converted with `NUM2DBL` and rejected if NaN or Infinity.
74
-
75
- ## Implemented group: segments
76
-
77
- `TG::Geometry::Segment` is a frozen value wrapper over one copied `struct tg_segment`.
78
-
79
- Created by:
80
-
81
- - `TG::Geometry::Line#segment_at(index)`
82
- - `TG::Geometry::Line#segments`
83
- - `TG::Geometry::Ring#segment_at(index)`
84
- - `TG::Geometry::Ring#segments`
85
-
86
- Methods:
87
-
88
- - `a -> [Float, Float]`
89
- - `b -> [Float, Float]`
90
- - `points -> [[Float, Float], [Float, Float]]`
91
- - `bbox -> TG::Geometry::Rect`
92
- - `intersects?(other_segment)`
93
-
94
- `TG::Geometry::Segment.allocate` is disabled. Segment wrappers do not borrow parent TG memory and do not call any TG free function.
95
-
96
- ## Still not implemented
97
-
98
- The following remain outside this grouped implementation:
99
-
100
- - Line/Ring/Polygon public constructors from Ruby arrays;
101
- - MultiLineString / MultiPolygon constructors from low-level wrappers;
102
- - callback/search APIs;
103
- - nearest segment APIs;
104
- - environment configuration functions;
105
- - global allocator override;
106
- - mutable settings;
107
- - user callbacks inside C loops.
108
-
109
- Those require separate ownership and callback-safety contracts before implementation.
@@ -1,121 +0,0 @@
1
- # Low-level geometry wrappers
2
-
3
- This document covers Expansion Block E.
4
-
5
- ## Scope
6
-
7
- The first low-level API exposes borrowed read-only views over TG child types:
8
-
9
- - `TG::Geometry::Line`
10
- - `TG::Geometry::Ring`
11
- - `TG::Geometry::Polygon`
12
- - `TG::Geometry::Segment`
13
-
14
- These wrappers are created only from `TG::Geometry::Geom`, `TG::Geometry::Polygon`, `TG::Geometry::Line`, and `TG::Geometry::Ring` methods. Public `.allocate` is disabled for all four classes.
15
-
16
- ## Ownership model
17
-
18
- TG child accessors return borrowed pointers owned by the parent `struct tg_geom`. The Line/Ring/Polygon Ruby wrappers therefore store:
19
-
20
- - `geom_owner`: the original `TG::Geometry::Geom` Ruby object;
21
- - a borrowed `const struct tg_line *`, `const struct tg_ring *`, or `const struct tg_poly *` pointer.
22
-
23
- The wrappers mark `geom_owner` with `rb_gc_mark_movable` and update it in `dcompact` with `rb_gc_location`. They do not call `tg_line_free`, `tg_ring_free`, or `tg_poly_free` for borrowed children.
24
-
25
- `TG::Geometry::Segment` is a value wrapper over a copied `struct tg_segment`, so it does not store `geom_owner` and does not free any TG-owned pointer.
26
-
27
- Allocator pairs:
28
-
29
- Resource | Allocator | Deallocator | Owner | Notes
30
- --- | --- | --- | --- | ---
31
- `tg_line_wrapper_t` | `TypedData_Make_Struct` / Ruby allocator | `ruby_xfree` | Ruby object | borrowed pointer, marks parent `geom_owner`
32
- `tg_ring_wrapper_t` | `TypedData_Make_Struct` / Ruby allocator | `ruby_xfree` | Ruby object | borrowed pointer, marks parent `geom_owner`
33
- `tg_polygon_wrapper_t` | `TypedData_Make_Struct` / Ruby allocator | `ruby_xfree` | Ruby object | borrowed pointer, marks parent `geom_owner`
34
- `tg_segment_wrapper_t` | `TypedData_Make_Struct` / Ruby allocator | `ruby_xfree` | Ruby object | owns copied `struct tg_segment` value
35
- TG child pointers | parent `struct tg_geom` | parent `TG::Geometry::Geom` dfree | parent geometry | never freed by child wrappers
36
-
37
- ## Public API
38
-
39
- ### `TG::Geometry::Geom#point`
40
-
41
- Returns `[x, y]` for point geometries. Returns `nil` for non-point geometries.
42
-
43
- ### `TG::Geometry::Geom#line`
44
-
45
- Returns a frozen `TG::Geometry::Line` for LineString geometries. Returns `nil` otherwise.
46
-
47
- ### `TG::Geometry::Geom#polygon`
48
-
49
- Returns a frozen `TG::Geometry::Polygon` for Polygon geometries. Returns `nil` otherwise.
50
-
51
- ### `TG::Geometry::Line`
52
-
53
- Methods:
54
-
55
- - `bbox -> TG::Geometry::Rect`
56
- - `num_points -> Integer`
57
- - `point_at(index) -> [Float, Float]`
58
- - `points -> Array<[Float, Float]>`
59
- - `num_segments -> Integer`
60
- - `segment_at(index) -> TG::Geometry::Segment`
61
- - `segments -> Array<TG::Geometry::Segment>`
62
- - `length -> Float`
63
- - `clockwise? -> Boolean`
64
-
65
- `length` is measured in input coordinate units. For lon/lat data this is not meters.
66
-
67
- ### `TG::Geometry::Ring`
68
-
69
- Methods:
70
-
71
- - `bbox -> TG::Geometry::Rect`
72
- - `num_points -> Integer`
73
- - `point_at(index) -> [Float, Float]`
74
- - `points -> Array<[Float, Float]>`
75
- - `num_segments -> Integer`
76
- - `segment_at(index) -> TG::Geometry::Segment`
77
- - `segments -> Array<TG::Geometry::Segment>`
78
- - `area -> Float`
79
- - `perimeter -> Float`
80
- - `clockwise? -> Boolean`
81
- - `convex? -> Boolean`
82
-
83
- `area` and `perimeter` are measured in input coordinate units. For lon/lat data these are not square meters or meters.
84
-
85
- ### `TG::Geometry::Polygon`
86
-
87
- Methods:
88
-
89
- - `bbox -> TG::Geometry::Rect`
90
- - `exterior_ring -> TG::Geometry::Ring`
91
- - `num_holes -> Integer`
92
- - `hole_at(index) -> TG::Geometry::Ring`
93
- - `holes -> Array<TG::Geometry::Ring>`
94
- - `clockwise? -> Boolean`
95
-
96
- `hole_at` rejects out-of-range indexes with `TG::Geometry::ArgumentError`.
97
-
98
- ### `TG::Geometry::Segment`
99
-
100
- Methods:
101
-
102
- - `a -> [Float, Float]`
103
- - `b -> [Float, Float]`
104
- - `points -> [[Float, Float], [Float, Float]]`
105
- - `bbox -> TG::Geometry::Rect`
106
- - `intersects?(other_segment) -> Boolean`
107
-
108
- Segments are copied by value from a line or ring. They do not keep or free borrowed TG pointers.
109
-
110
- ## Error paths
111
-
112
- - Wrong wrapper type is handled by `TypedData_Get_Struct` and raises `TypeError`.
113
- - Out-of-range child indexes raise `TG::Geometry::ArgumentError`.
114
- - Child wrappers are immutable and cannot be constructed directly from Ruby.
115
-
116
- ## Not implemented in this block
117
-
118
- - Low-level constructors for Ring, Line, or Polygon.
119
- - Mutable coordinate access.
120
- - Polygon `area` / `perimeter` convenience aggregation. Ring-level values are exposed first to avoid undocumented assumptions about hole orientation and aggregation semantics.
121
- - Geodesic length/area.
data/docs/RACTOR.md DELETED
@@ -1,40 +0,0 @@
1
- # Ractor investigation
2
-
3
- This document covers Expansion Block I.
4
-
5
- ## Current result
6
-
7
- `tg_geometry` does not claim Ractor support.
8
-
9
- The current native wrappers are frozen and immutable for normal Ruby thread reads, but they are not Ractor-shareable Ruby objects. The test suite records this boundary with `Ractor.shareable?` and `Ractor.make_shareable` checks for:
10
-
11
- - `TG::Geometry::Geom`
12
- - `TG::Geometry::Rect`
13
- - `TG::Geometry::Index`
14
-
15
- This is an explicit unsupported boundary, not a partial support claim.
16
-
17
- ## Shareability rules
18
-
19
- Current rule for public docs and code comments:
20
-
21
- - Normal Ruby thread read-only use is supported by immutable object design and tests.
22
- - Ractor support is not supported and not advertised.
23
- - Do not use `TG::Geometry::Geom`, `TG::Geometry::Rect`, `TG::Geometry::Index`, or borrowed low-level wrappers as cross-Ractor shareable objects.
24
- - Do not add Ractor-specific code paths without a new explicit design and tests.
25
-
26
- ## Why no support claim is made
27
-
28
- The extension stores native pointers and Ruby `VALUE` references inside TypedData wrappers. The current contract validates GC marking, compaction, ownership, and normal thread read-only access. It does not define or validate cross-Ractor transfer/share semantics for those wrappers.
29
-
30
- ## Required before changing this status
31
-
32
- Before any future Ractor support claim:
33
-
34
- 1. Define shareability rules for owned and borrowed `TG::Geometry::Geom` wrappers.
35
- 2. Define shareability rules for `TG::Geometry::Index` entries and id `VALUE`s.
36
- 3. Define behavior for borrowed Line/Ring/Polygon/Segment wrappers.
37
- 4. Add Ractor tests that pass on the supported Ruby matrix.
38
- 5. Document whether objects are shareable directly, require duplication, or are unsupported.
39
-
40
- Until those items exist, Ractor remains unsupported.
data/docs/REGISTRY.md DELETED
@@ -1,37 +0,0 @@
1
- # Registry
2
-
3
- `TG::Geometry::Registry` is Ruby-side application sugar over immutable `TG::Geometry::Index`.
4
-
5
- It does not add a mutable native index. `reload!` always builds a new Index and then swaps a Ruby reference:
6
-
7
- ```ruby
8
- class DeliveryZones < TG::Geometry::Registry
9
- source do
10
- [
11
- [:zone_a, '{"type":"Polygon","coordinates":[[[0,0],[10,0],[10,10],[0,10],[0,0]]]}']
12
- ]
13
- end
14
- end
15
-
16
- registry = DeliveryZones.new(via: :geojson, strategy: :rtree)
17
- registry.reload!
18
- registry.find_covering(5, 5)
19
- ```
20
-
21
- Readers capture the current immutable Index. An active reader can continue using the old Index while `reload!` builds and swaps a new one.
22
-
23
- ## API
24
-
25
- - `source { ... }` on a subclass defines the entry source.
26
- - `index_options(...)` on a subclass sets default Index options.
27
- - `reload!` builds and swaps a new Index.
28
- - `find_covering`, `covering_ids`, `intersecting_rect`, and `covering_ids_batch_packed` delegate to the current Index.
29
- - `index`, `size`, `bbox`, and `loaded?` expose current state.
30
-
31
- The source must return the strict first-release entry format:
32
-
33
- ```ruby
34
- [[id, object], [id, object], ...]
35
- ```
36
-
37
- No Redis dependency, no Rails dependency, no global native singleton, and no in-place Index mutation are introduced.
@@ -1,39 +0,0 @@
1
- # First public release checklist
2
-
3
- This checklist is intentionally conservative. Do not publish until every required item is satisfied.
4
-
5
- ## Required state before release
6
-
7
- - [ ] All release-core specs pass locally.
8
- - [ ] GC.stress specs pass.
9
- - [ ] GC.compact specs pass.
10
- - [ ] ASAN status is documented.
11
- - [ ] Valgrind status is documented.
12
- - [ ] Benchmark scripts exist and have been run for the full scenario matrix.
13
- - [ ] README and docs are complete.
14
- - [ ] Gem metadata is final:
15
- - gem name: `tg_geometry`;
16
- - author: `Roman Haydarov`;
17
- - email: `romnhajdarov@gmail.com`;
18
- - repository: `https://github.com/roman-haidarov/tg_geometry`.
19
- - [ ] Vendored TG and rtree versions are pinned in `VERSION` files.
20
- - [ ] Upstream license files are included under `ext/tg_geometry/vendor/*/LICENSE`.
21
- - [ ] `CHANGELOG.md` exists.
22
-
23
- ## Do not release if any of these are true
24
-
25
- - [ ] Any OPEN QUESTION affects correctness.
26
- - [ ] Any memory accounting path is approximate.
27
- - [ ] Any failed build path waits for GC instead of immediate dispose.
28
- - [ ] `strategy: :auto` is enabled without benchmark-derived threshold.
29
- - [ ] Public API differs from the contract without Roman approval.
30
- - [ ] Ractor support is claimed.
31
- - [ ] Performance claims are present without project-owned benchmark output.
32
-
33
- ## OPEN QUESTION: ASAN setup
34
-
35
- ASAN setup is not fully specified in the contract and the worker must not research it independently. The repository contains a placeholder CI job that fails with an explicit message until Roman approves the exact ASAN approach.
36
-
37
- ## OPEN QUESTION: Valgrind setup
38
-
39
- Valgrind setup can vary by CI image and Ruby build. The repository contains a placeholder CI job that installs/runs nothing by default and asks for approval before adding a final Valgrind configuration.