tg_geometry 0.1.0 → 0.2.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +18 -79
- data/README.md +82 -191
- data/Rakefile +3 -3
- data/benchmark/falcon_concurrency.rb +1 -1
- data/benchmark/feature_source.rb +92 -0
- data/docs/ARCHITECTURE.md +29 -107
- data/docs/BENCHMARKING.md +20 -1
- data/docs/CASUAL_EXAMPLE.md +71 -458
- data/docs/CONCURRENCY.md +13 -7
- data/docs/ERROR_HANDLING.md +30 -0
- data/docs/FEATURE_SOURCE.md +166 -0
- data/docs/LIMITATIONS.md +11 -50
- data/docs/MEMORY_OWNERSHIP.md +20 -2
- data/ext/tg_geometry/extconf.rb +46 -4
- data/ext/tg_geometry/tg_geometry_ext.c +2453 -150
- data/ext/tg_geometry/tg_geometry_vendor_json.c +17 -0
- data/ext/tg_geometry/tg_geometry_vendor_tg.c +3 -0
- data/ext/tg_geometry/vendor/.vendored +8 -2
- data/ext/tg_geometry/vendor/json/LICENSE +20 -0
- data/ext/tg_geometry/vendor/json/VERSION +3 -0
- data/ext/tg_geometry/vendor/json/json.c +1024 -0
- data/ext/tg_geometry/vendor/json/json.h +207 -0
- data/lib/tg/geometry/registry.rb +3 -3
- data/lib/tg/geometry/version.rb +1 -1
- data/script/vendor_libs.rb +22 -6
- data/spec/{expansion_a_auto_strategy_spec.rb → auto_strategy_spec.rb} +1 -1
- data/spec/{block_12_batch_packed_spec.rb → batch_packed_spec.rb} +1 -1
- data/spec/{block_20_concurrency_spec.rb → concurrency_spec.rb} +1 -1
- data/spec/{block_13_error_hardening_spec.rb → error_hardening_spec.rb} +1 -1
- data/spec/feature_source_nogvl_spec.rb +51 -0
- data/spec/feature_source_spec.rb +268 -0
- data/spec/{expansion_d_format_coverage_spec.rb → format_coverage_spec.rb} +1 -1
- data/spec/{block_20_fuzz_spec.rb → fuzz_spec.rb} +1 -1
- data/spec/{block_4_geom_api_spec.rb → geom_api_spec.rb} +1 -1
- data/spec/{block_3_geom_parse_spec.rb → geom_parse_spec.rb} +1 -1
- data/spec/{block_8_index_borrowed_geometry_spec.rb → index_borrowed_geometry_spec.rb} +1 -1
- data/spec/{block_6_index_build_spec.rb → index_build_spec.rb} +2 -2
- data/spec/{block_9_flat_query_spec.rb → index_flat_query_spec.rb} +1 -1
- data/spec/{block_7_index_owned_geometry_spec.rb → index_owned_geometry_spec.rb} +1 -1
- data/spec/{block_10_rtree_strategy_spec.rb → index_rtree_accounting_spec.rb} +1 -1
- data/spec/{block_11_rtree_order_spec.rb → index_rtree_order_spec.rb} +1 -1
- data/spec/{block_1_skeleton_spec.rb → load_and_errors_spec.rb} +1 -1
- data/spec/{expansion_e_low_level_geometry_spec.rb → low_level_geometry_spec.rb} +1 -1
- data/spec/{block_14_memory_gc_hardening_spec.rb → memory_gc_spec.rb} +1 -1
- data/spec/{expansion_i_ractor_spec.rb → ractor_spec.rb} +1 -1
- data/spec/{block_5_rect_api_spec.rb → rect_api_spec.rb} +1 -1
- data/spec/{expansion_b_registry_spec.rb → registry_spec.rb} +1 -1
- data/spec/{expansion_j_full_tg_api_coverage_spec.rb → tg_api_coverage_spec.rb} +1 -1
- data/spec/{block_2_vendor_spec.rb → vendor_sources_spec.rb} +4 -4
- metadata +39 -38
- data/docs/ACTIVE_RECORD.md +0 -26
- data/docs/AUTO_STRATEGY.md +0 -15
- data/docs/EXPANSION_E_TO_H_STATUS.md +0 -51
- data/docs/FORMAT_COVERAGE.md +0 -23
- data/docs/FULL_TG_API_COVERAGE.md +0 -109
- data/docs/LOW_LEVEL_GEOMETRY.md +0 -121
- data/docs/RACTOR.md +0 -40
- data/docs/REGISTRY.md +0 -37
- data/docs/RELEASE_CHECKLIST.md +0 -39
- /data/spec/{expansion_c_active_record_source_spec.rb → active_record_source_spec.rb} +0 -0
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
require "spec_helper"
|
|
4
4
|
require "objspace"
|
|
5
5
|
|
|
6
|
-
RSpec.describe "
|
|
6
|
+
RSpec.describe "Geom parsing" do
|
|
7
7
|
let(:geojson_point) { %({"type":"Point","coordinates":[1.0,2.0]}) }
|
|
8
8
|
let(:wkt_point) { "POINT (1 2)" }
|
|
9
9
|
let(:wkb_point) { [1, 1].pack("CL<") + [1.0, 2.0].pack("E2") }
|
|
@@ -5,7 +5,7 @@ require "json"
|
|
|
5
5
|
require "objspace"
|
|
6
6
|
require "weakref"
|
|
7
7
|
|
|
8
|
-
RSpec.describe "
|
|
8
|
+
RSpec.describe "Index borrowed geometry ingestion" do
|
|
9
9
|
let(:polygon_a_wkt) { "POLYGON ((0 0, 2 0, 2 2, 0 2, 0 0))" }
|
|
10
10
|
let(:polygon_b_wkt) { "POLYGON ((10 10, 12 10, 12 12, 10 12, 10 10))" }
|
|
11
11
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
require "spec_helper"
|
|
4
4
|
|
|
5
|
-
RSpec.describe "
|
|
5
|
+
RSpec.describe "Index build" do
|
|
6
6
|
let(:geom) { TG::Geometry.parse_wkt("POINT (1 2)") }
|
|
7
7
|
|
|
8
8
|
it "builds an empty immutable index" do
|
|
@@ -54,7 +54,7 @@ RSpec.describe "Release Core Block 6 Index build skeleton" do
|
|
|
54
54
|
.to raise_error(TG::Geometry::ArgumentError)
|
|
55
55
|
end
|
|
56
56
|
|
|
57
|
-
it "requires String values for owned geometry modes in the
|
|
57
|
+
it "requires String values for owned geometry modes in the owned geometry build path" do
|
|
58
58
|
expect { TG::Geometry::Index.build([[1, Object.new]], via: :geojson, strategy: :flat) }
|
|
59
59
|
.to raise_error(TypeError)
|
|
60
60
|
expect { TG::Geometry::Index.build([[1, Object.new]], via: :wkb, strategy: :flat) }
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
require "spec_helper"
|
|
4
4
|
|
|
5
|
-
RSpec.describe "
|
|
5
|
+
RSpec.describe "Flat index query engine" do
|
|
6
6
|
let(:zone_a) { TG::Geometry.parse_wkt("POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))") }
|
|
7
7
|
let(:zone_b) { TG::Geometry.parse_wkt("POLYGON ((5 5, 12 5, 12 12, 5 12, 5 5))") }
|
|
8
8
|
let(:zone_c) { TG::Geometry.parse_wkt("POLYGON ((20 20, 22 20, 22 22, 20 22, 20 20))") }
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
require "spec_helper"
|
|
4
4
|
require "objspace"
|
|
5
5
|
|
|
6
|
-
RSpec.describe "
|
|
6
|
+
RSpec.describe "Index owned geometry ingestion" do
|
|
7
7
|
let(:geojson_a) do
|
|
8
8
|
'{"type":"Polygon","coordinates":[[[0,0],[2,0],[2,2],[0,2],[0,0]]]}'
|
|
9
9
|
end
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
require "spec_helper"
|
|
4
4
|
|
|
5
|
-
RSpec.describe "
|
|
5
|
+
RSpec.describe "Exact rtree accounting" do
|
|
6
6
|
let(:zone_a) { TG::Geometry.parse_wkt("POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))") }
|
|
7
7
|
let(:zone_b) { TG::Geometry.parse_wkt("POLYGON ((20 20, 30 20, 30 30, 20 30, 20 20))") }
|
|
8
8
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
require "spec_helper"
|
|
4
4
|
|
|
5
|
-
RSpec.describe "
|
|
5
|
+
RSpec.describe "Low-level geometry wrappers" do
|
|
6
6
|
it "exposes Point coordinates without constructing a mutable point wrapper" do
|
|
7
7
|
geom = TG::Geometry.parse_wkt("POINT (1 2)")
|
|
8
8
|
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
require "spec_helper"
|
|
4
4
|
require "objspace"
|
|
5
5
|
|
|
6
|
-
RSpec.describe "
|
|
6
|
+
RSpec.describe "Memory, GC, and compaction hardening" do
|
|
7
7
|
let(:small_polygon_wkt) { "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))" }
|
|
8
8
|
let(:small_polygon_geojson) { '{"type":"Polygon","coordinates":[[[0,0],[10,0],[10,10],[0,10],[0,0]]]}' }
|
|
9
9
|
let(:malformed_geojson) { '{"type":"Polygon","coordinates":[' }
|
|
@@ -26,7 +26,7 @@ RSpec.describe TG::Geometry::Registry do
|
|
|
26
26
|
expect(registry.find_covering(25, 25)).to eq(:b)
|
|
27
27
|
end
|
|
28
28
|
|
|
29
|
-
it "supports subclass source
|
|
29
|
+
it "supports subclass source definitions" do
|
|
30
30
|
entries = [[:a, zone_a]]
|
|
31
31
|
klass = Class.new(described_class) do
|
|
32
32
|
source { entries }
|
|
@@ -4,16 +4,16 @@ require "spec_helper"
|
|
|
4
4
|
require "open3"
|
|
5
5
|
require "tmpdir"
|
|
6
6
|
|
|
7
|
-
RSpec.describe "
|
|
7
|
+
RSpec.describe "Vendored source build" do
|
|
8
8
|
let(:vendor_dir) { File.join(ROOT, "ext", "tg_geometry", "vendor") }
|
|
9
9
|
|
|
10
|
-
it "keeps tidwall/tg vendored under the
|
|
10
|
+
it "keeps tidwall/tg vendored under the expected path" do
|
|
11
11
|
%w[tg.c tg.h VERSION].each do |name|
|
|
12
12
|
expect(File.file?(File.join(vendor_dir, "tg", name))).to be(true), "missing vendor/tg/#{name}"
|
|
13
13
|
end
|
|
14
14
|
end
|
|
15
15
|
|
|
16
|
-
it "keeps rtree.c vendored under the
|
|
16
|
+
it "keeps rtree.c vendored under the expected path" do
|
|
17
17
|
%w[rtree.c rtree.h VERSION].each do |name|
|
|
18
18
|
expect(File.file?(File.join(vendor_dir, "rtree", name))).to be(true), "missing vendor/rtree/#{name}"
|
|
19
19
|
end
|
|
@@ -51,7 +51,7 @@ RSpec.describe "Release Core Block 2 vendored build" do
|
|
|
51
51
|
end
|
|
52
52
|
end
|
|
53
53
|
|
|
54
|
-
it "uses the
|
|
54
|
+
it "uses the expected compiler warning flags" do
|
|
55
55
|
extconf = File.read(File.join(ROOT, "ext", "tg_geometry", "extconf.rb"))
|
|
56
56
|
|
|
57
57
|
expect(extconf).to include('"-std=c11"')
|
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.
|
|
4
|
+
version: 0.2.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-
|
|
11
|
+
date: 2026-05-26 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: rake
|
|
@@ -44,8 +44,9 @@ description: Defines TG::Geometry with immutable Geom parsing and constructor wr
|
|
|
44
44
|
wrappers, value Segment wrappers, Registry reload sugar, optional ActiveRecord source
|
|
45
45
|
helpers, and an immutable geofencing-oriented Index with owned and borrowed geometry
|
|
46
46
|
ingestion, flat/rtree strategies, deterministic ordered id results, exact rtree
|
|
47
|
-
allocation accounting, and native-endian packed point batch queries
|
|
48
|
-
|
|
47
|
+
allocation accounting, and native-endian packed point batch queries, and FeatureSource
|
|
48
|
+
GeoJSON FeatureCollection extraction/build paths over vendored C sources. Ractor
|
|
49
|
+
support is not claimed.
|
|
49
50
|
email:
|
|
50
51
|
- romnhajdarov@gmail.com
|
|
51
52
|
executables: []
|
|
@@ -61,32 +62,30 @@ files:
|
|
|
61
62
|
- benchmark/_support.rb
|
|
62
63
|
- benchmark/batch_packed_vs_loop.rb
|
|
63
64
|
- benchmark/falcon_concurrency.rb
|
|
65
|
+
- benchmark/feature_source.rb
|
|
64
66
|
- benchmark/flat_vs_rtree.rb
|
|
65
67
|
- benchmark/gvl_threshold.rb
|
|
66
68
|
- benchmark/objectspace_memsize.rb
|
|
67
69
|
- benchmark/parse_throughput.rb
|
|
68
70
|
- benchmark/rss_stability.rb
|
|
69
|
-
- docs/ACTIVE_RECORD.md
|
|
70
71
|
- docs/ARCHITECTURE.md
|
|
71
|
-
- docs/AUTO_STRATEGY.md
|
|
72
72
|
- docs/BENCHMARKING.md
|
|
73
73
|
- docs/CASUAL_EXAMPLE.md
|
|
74
74
|
- docs/CONCURRENCY.md
|
|
75
75
|
- docs/ERROR_HANDLING.md
|
|
76
|
-
- docs/
|
|
77
|
-
- docs/FORMAT_COVERAGE.md
|
|
78
|
-
- docs/FULL_TG_API_COVERAGE.md
|
|
76
|
+
- docs/FEATURE_SOURCE.md
|
|
79
77
|
- docs/LIMITATIONS.md
|
|
80
|
-
- docs/LOW_LEVEL_GEOMETRY.md
|
|
81
78
|
- docs/MEMORY_OWNERSHIP.md
|
|
82
|
-
- docs/RACTOR.md
|
|
83
|
-
- docs/REGISTRY.md
|
|
84
|
-
- docs/RELEASE_CHECKLIST.md
|
|
85
79
|
- ext/tg_geometry/extconf.rb
|
|
86
80
|
- ext/tg_geometry/tg_geometry_ext.c
|
|
81
|
+
- ext/tg_geometry/tg_geometry_vendor_json.c
|
|
87
82
|
- ext/tg_geometry/tg_geometry_vendor_rtree.c
|
|
88
83
|
- ext/tg_geometry/tg_geometry_vendor_tg.c
|
|
89
84
|
- ext/tg_geometry/vendor/.vendored
|
|
85
|
+
- ext/tg_geometry/vendor/json/LICENSE
|
|
86
|
+
- ext/tg_geometry/vendor/json/VERSION
|
|
87
|
+
- ext/tg_geometry/vendor/json/json.c
|
|
88
|
+
- ext/tg_geometry/vendor/json/json.h
|
|
90
89
|
- ext/tg_geometry/vendor/rtree/LICENSE
|
|
91
90
|
- ext/tg_geometry/vendor/rtree/README.md
|
|
92
91
|
- ext/tg_geometry/vendor/rtree/VERSION
|
|
@@ -103,30 +102,32 @@ files:
|
|
|
103
102
|
- lib/tg/geometry/version.rb
|
|
104
103
|
- lib/tg_geometry.rb
|
|
105
104
|
- script/vendor_libs.rb
|
|
106
|
-
- spec/
|
|
107
|
-
- spec/
|
|
108
|
-
- spec/
|
|
109
|
-
- spec/
|
|
110
|
-
- spec/
|
|
111
|
-
- spec/
|
|
112
|
-
- spec/
|
|
113
|
-
- spec/
|
|
114
|
-
- spec/
|
|
115
|
-
- spec/
|
|
116
|
-
- spec/
|
|
117
|
-
- spec/
|
|
118
|
-
- spec/
|
|
119
|
-
- spec/
|
|
120
|
-
- spec/
|
|
121
|
-
- spec/
|
|
122
|
-
- spec/
|
|
123
|
-
- spec/
|
|
124
|
-
- spec/
|
|
125
|
-
- spec/
|
|
126
|
-
- spec/
|
|
127
|
-
- spec/
|
|
128
|
-
- spec/
|
|
105
|
+
- spec/active_record_source_spec.rb
|
|
106
|
+
- spec/auto_strategy_spec.rb
|
|
107
|
+
- spec/batch_packed_spec.rb
|
|
108
|
+
- spec/concurrency_spec.rb
|
|
109
|
+
- spec/error_hardening_spec.rb
|
|
110
|
+
- spec/feature_source_nogvl_spec.rb
|
|
111
|
+
- spec/feature_source_spec.rb
|
|
112
|
+
- spec/format_coverage_spec.rb
|
|
113
|
+
- spec/fuzz_spec.rb
|
|
114
|
+
- spec/geom_api_spec.rb
|
|
115
|
+
- spec/geom_parse_spec.rb
|
|
116
|
+
- spec/index_borrowed_geometry_spec.rb
|
|
117
|
+
- spec/index_build_spec.rb
|
|
118
|
+
- spec/index_flat_query_spec.rb
|
|
119
|
+
- spec/index_owned_geometry_spec.rb
|
|
120
|
+
- spec/index_rtree_accounting_spec.rb
|
|
121
|
+
- spec/index_rtree_order_spec.rb
|
|
122
|
+
- spec/load_and_errors_spec.rb
|
|
123
|
+
- spec/low_level_geometry_spec.rb
|
|
124
|
+
- spec/memory_gc_spec.rb
|
|
125
|
+
- spec/ractor_spec.rb
|
|
126
|
+
- spec/rect_api_spec.rb
|
|
127
|
+
- spec/registry_spec.rb
|
|
129
128
|
- spec/spec_helper.rb
|
|
129
|
+
- spec/tg_api_coverage_spec.rb
|
|
130
|
+
- spec/vendor_sources_spec.rb
|
|
130
131
|
homepage: https://github.com/roman-haidarov/tg_geometry
|
|
131
132
|
licenses:
|
|
132
133
|
- MIT
|
|
@@ -142,7 +143,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
142
143
|
requirements:
|
|
143
144
|
- - ">="
|
|
144
145
|
- !ruby/object:Gem::Version
|
|
145
|
-
version: '3.
|
|
146
|
+
version: '3.1'
|
|
146
147
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
147
148
|
requirements:
|
|
148
149
|
- - ">="
|
|
@@ -153,5 +154,5 @@ rubygems_version: 3.3.27
|
|
|
153
154
|
signing_key:
|
|
154
155
|
specification_version: 4
|
|
155
156
|
summary: Native extension for TG::Geometry parsing, predicates, immutable indexes,
|
|
156
|
-
registries, low-level wrappers, and packed point batches
|
|
157
|
+
FeatureSource imports, registries, low-level wrappers, and packed point batches
|
|
157
158
|
test_files: []
|
data/docs/ACTIVE_RECORD.md
DELETED
|
@@ -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.
|
data/docs/AUTO_STRATEGY.md
DELETED
|
@@ -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.
|
data/docs/FORMAT_COVERAGE.md
DELETED
|
@@ -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.
|
data/docs/LOW_LEVEL_GEOMETRY.md
DELETED
|
@@ -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.
|