tg_geometry 0.3.1 → 0.3.2
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 +21 -0
- data/README.md +41 -1
- data/benchmark/_support.rb +11 -4
- data/benchmark/distance_memory_accounting.rb +46 -0
- data/benchmark/distance_point_geom.rb +49 -0
- data/benchmark/distance_within_radius.rb +75 -0
- data/docs/BENCHMARKING.md +28 -1
- data/docs/CONCURRENCY.md +8 -0
- data/docs/GEOMETRY_QUERIES.md +42 -0
- data/docs/LIMITATIONS.md +12 -0
- data/ext/tg_geometry/tg_geometry_ext.c +840 -0
- data/lib/tg/geometry/version.rb +1 -1
- data/lib/tg/geometry.rb +40 -0
- data/spec/distance_spec.rb +258 -0
- metadata +11 -6
data/lib/tg/geometry/version.rb
CHANGED
data/lib/tg/geometry.rb
CHANGED
|
@@ -43,6 +43,30 @@ module TG
|
|
|
43
43
|
# @!method srid
|
|
44
44
|
# @return [Integer, nil] SRID metadata; not used for reprojection
|
|
45
45
|
#
|
|
46
|
+
# @!method distance_to_lnglat_meters(lng, lat)
|
|
47
|
+
# Approximate meters in a query-local equirectangular frame. Not geodesy.
|
|
48
|
+
# @return [Float]
|
|
49
|
+
#
|
|
50
|
+
# @!method boundary_distance_to_lnglat_meters(lng, lat)
|
|
51
|
+
# Approximate meters to nearest boundary/segment/point.
|
|
52
|
+
# @return [Float]
|
|
53
|
+
#
|
|
54
|
+
# @!method nearest_point_lnglat(lng, lat)
|
|
55
|
+
# Raw planar nearest boundary/geometry point. Longitude is not wrapped.
|
|
56
|
+
# @return [Array(Float, Float)]
|
|
57
|
+
#
|
|
58
|
+
# @!method distance_to_xy(x, y)
|
|
59
|
+
# Planar distance in input coordinate units.
|
|
60
|
+
# @return [Float]
|
|
61
|
+
#
|
|
62
|
+
# @!method boundary_distance_to_xy(x, y)
|
|
63
|
+
# Planar boundary distance in input coordinate units.
|
|
64
|
+
# @return [Float]
|
|
65
|
+
#
|
|
66
|
+
# @!method nearest_point_xy(x, y)
|
|
67
|
+
# Planar nearest boundary/geometry point in input coordinate units.
|
|
68
|
+
# @return [Array(Float, Float)]
|
|
69
|
+
#
|
|
46
70
|
# @!method to_ewkb(srid: nil)
|
|
47
71
|
# Writes EWKB with the SRID flag set. Uses explicit srid: when provided,
|
|
48
72
|
# otherwise Geom#srid. Raises if no SRID is available. to_wkb remains plain.
|
|
@@ -68,6 +92,22 @@ module TG
|
|
|
68
92
|
# Stored geometries for which tg_geom_contains(stored, query) is true.
|
|
69
93
|
# Direction: stored contains query. Boundary points are not contained.
|
|
70
94
|
# @return [Array<Object>] ids in insertion order
|
|
95
|
+
#
|
|
96
|
+
# @!method within_distance_lnglat_meters(lng, lat, radius_m, sort: false)
|
|
97
|
+
# Rtree bbox prefilter plus exact approximate-meter distance filter.
|
|
98
|
+
# @return [Array<Array(Object, Float)>]
|
|
99
|
+
#
|
|
100
|
+
# @!method within_distance_ids_lnglat_meters(lng, lat, radius_m)
|
|
101
|
+
# Same membership as within_distance_lnglat_meters, ids only.
|
|
102
|
+
# @return [Array<Object>]
|
|
103
|
+
#
|
|
104
|
+
# @!method within_distance_xy(x, y, radius, sort: false)
|
|
105
|
+
# Rtree bbox prefilter plus exact planar distance filter.
|
|
106
|
+
# @return [Array<Array(Object, Float)>]
|
|
107
|
+
#
|
|
108
|
+
# @!method within_distance_ids_xy(x, y, radius)
|
|
109
|
+
# Same membership as within_distance_xy, ids only.
|
|
110
|
+
# @return [Array<Object>]
|
|
71
111
|
end
|
|
72
112
|
|
|
73
113
|
class Line
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "spec_helper"
|
|
4
|
+
|
|
5
|
+
RSpec.describe "point to geometry distance" do
|
|
6
|
+
R = 6_371_008.8
|
|
7
|
+
DEG = Math::PI / 180.0
|
|
8
|
+
|
|
9
|
+
def meters_per_lng_at(lat)
|
|
10
|
+
R * DEG * Math.cos(lat * DEG)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def meters_per_lat
|
|
14
|
+
R * DEG
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def point_to_segment_distance(px, py, ax, ay, bx, by)
|
|
18
|
+
dx = bx - ax
|
|
19
|
+
dy = by - ay
|
|
20
|
+
len_sq = dx * dx + dy * dy
|
|
21
|
+
return Math.hypot(px - ax, py - ay) if len_sq.zero?
|
|
22
|
+
|
|
23
|
+
t = (((px - ax) * dx) + ((py - ay) * dy)) / len_sq
|
|
24
|
+
t = 0.0 if t < 0.0
|
|
25
|
+
t = 1.0 if t > 1.0
|
|
26
|
+
Math.hypot(px - (ax + t * dx), py - (ay + t * dy))
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def lnglat_segment_distance_m(query_lng, query_lat, a, b)
|
|
30
|
+
sx = meters_per_lng_at(query_lat)
|
|
31
|
+
sy = meters_per_lat
|
|
32
|
+
ax = sx * (a[0] - query_lng)
|
|
33
|
+
ay = sy * (a[1] - query_lat)
|
|
34
|
+
bx = sx * (b[0] - query_lng)
|
|
35
|
+
by = sy * (b[1] - query_lat)
|
|
36
|
+
point_to_segment_distance(0.0, 0.0, ax, ay, bx, by)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
it "implements polygon inside, boundary, outside, and hole semantics" do
|
|
40
|
+
polygon = TG::Geometry.polygon(
|
|
41
|
+
[[0, 0], [10, 0], [10, 10], [0, 10], [0, 0]],
|
|
42
|
+
holes: [[[2, 2], [4, 2], [4, 4], [2, 4], [2, 2]]]
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
expect(polygon.distance_to_xy(5, 5)).to eq(0.0)
|
|
46
|
+
expect(polygon.boundary_distance_to_xy(5, 5)).to be_within(1e-12).of(Math.sqrt(2.0))
|
|
47
|
+
expect(polygon.distance_to_xy(0, 5)).to eq(0.0)
|
|
48
|
+
expect(polygon.boundary_distance_to_xy(0, 5)).to eq(0.0)
|
|
49
|
+
expect(polygon.distance_to_xy(12, 5)).to be_within(1e-12).of(2.0)
|
|
50
|
+
expect(polygon.distance_to_xy(3, 3)).to be_within(1e-12).of(1.0)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
it "uses approximate local meters for lng/lat outside distances" do
|
|
54
|
+
polygon = TG::Geometry.polygon([[0, 0], [0.01, 0], [0.01, 0.01], [0, 0.01], [0, 0]])
|
|
55
|
+
distance = polygon.distance_to_lnglat_meters(0.02, 0.005)
|
|
56
|
+
expected = meters_per_lng_at(0.005) * 0.01
|
|
57
|
+
|
|
58
|
+
expect(distance).to be_within(0.01).of(expected)
|
|
59
|
+
expect(polygon.distance_to_lnglat_meters(0.005, 0.005)).to eq(0.0)
|
|
60
|
+
expect(polygon.boundary_distance_to_lnglat_meters(0.005, 0.005)).to be > 0.0
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
it "uses minimum distance over multipolygon members" do
|
|
64
|
+
multipolygon = TG::Geometry.parse_wkt(
|
|
65
|
+
"MULTIPOLYGON (((0 0, 2 0, 2 2, 0 2, 0 0)), ((10 0, 12 0, 12 2, 10 2, 10 0)))"
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
expect(multipolygon.distance_to_xy(1, 1)).to eq(0.0)
|
|
69
|
+
expect(multipolygon.distance_to_xy(6, 1)).to be_within(1e-12).of(4.0)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
it "handles lines, points, multipoints, and mixed geometry collections" do
|
|
73
|
+
line = TG::Geometry.line_string([[0, 0], [10, 0]])
|
|
74
|
+
point = TG::Geometry.point(3, 4)
|
|
75
|
+
multipoint = TG::Geometry.parse_wkt("MULTIPOINT ((0 0), (10 0))")
|
|
76
|
+
collection = TG::Geometry.parse_wkt(
|
|
77
|
+
"GEOMETRYCOLLECTION (POLYGON ((20 20, 30 20, 30 30, 20 30, 20 20)), LINESTRING (0 0, 10 0))"
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
expect(line.distance_to_xy(5, 3)).to be_within(1e-12).of(line.boundary_distance_to_xy(5, 3))
|
|
81
|
+
expect(line.distance_to_xy(5, 3)).to be_within(1e-12).of(3.0)
|
|
82
|
+
expect(point.distance_to_xy(0, 0)).to be_within(1e-12).of(5.0)
|
|
83
|
+
expect(multipoint.distance_to_xy(7, 0)).to be_within(1e-12).of(3.0)
|
|
84
|
+
expect(collection.distance_to_xy(5, 2)).to be_within(1e-12).of(2.0)
|
|
85
|
+
expect(collection.distance_to_xy(25, 25)).to eq(0.0)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
it "skips empty members but raises when nothing is measurable" do
|
|
89
|
+
mixed = TG::Geometry.parse_wkt("GEOMETRYCOLLECTION (POINT EMPTY, POINT (1 1))")
|
|
90
|
+
all_empty = TG::Geometry.parse_wkt("GEOMETRYCOLLECTION (POINT EMPTY, LINESTRING EMPTY)")
|
|
91
|
+
|
|
92
|
+
expect(mixed.distance_to_xy(4, 5)).to be_within(1e-12).of(5.0)
|
|
93
|
+
expect { all_empty.distance_to_xy(0, 0) }.to raise_error(TG::Geometry::ArgumentError)
|
|
94
|
+
expect { TG::Geometry.empty_polygon.boundary_distance_to_xy(0, 0) }.to raise_error(TG::Geometry::ArgumentError)
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
it "returns nearest boundary points for interior polygon queries" do
|
|
98
|
+
polygon = TG::Geometry.polygon([[0, 0], [10, 0], [10, 10], [0, 10], [0, 0]])
|
|
99
|
+
nearest = polygon.nearest_point_xy(5, 5)
|
|
100
|
+
boundary_distance = polygon.boundary_distance_to_xy(5, 5)
|
|
101
|
+
|
|
102
|
+
expect(polygon.distance_to_xy(5, 5)).to eq(0.0)
|
|
103
|
+
expect(boundary_distance).to be_within(1e-12).of(5.0)
|
|
104
|
+
expect(point_to_segment_distance(5, 5, nearest[0], nearest[1], nearest[0], nearest[1])).to be_within(1e-12).of(boundary_distance)
|
|
105
|
+
expect(nearest[0] == 0.0 || nearest[0] == 10.0 || nearest[1] == 0.0 || nearest[1] == 10.0).to be(true)
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
it "keeps nearest lng/lat raw and does not force-wrap returned longitude" do
|
|
109
|
+
line = TG::Geometry.line_string([[181, 0], [181, 1]])
|
|
110
|
+
nearest = line.nearest_point_lnglat(180, 0.5)
|
|
111
|
+
|
|
112
|
+
expect(nearest[0]).to be_within(1e-12).of(181.0)
|
|
113
|
+
expect(nearest[1]).to be_within(1e-12).of(0.5)
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
it "does not wrap antimeridian proximity" do
|
|
117
|
+
point = TG::Geometry.point(179.9, 0)
|
|
118
|
+
|
|
119
|
+
expect(point.distance_to_lnglat_meters(-179.9, 0)).to be > 30_000_000
|
|
120
|
+
expect(point.covers_xy?(-179.9, 0)).to be(false)
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
it "matches independent local-meter segment math and stays finite near the pole" do
|
|
124
|
+
line = TG::Geometry.line_string([[0, 0], [0.01, 0]])
|
|
125
|
+
expected = lnglat_segment_distance_m(0.005, 0.01, [0, 0], [0.01, 0])
|
|
126
|
+
|
|
127
|
+
expect(line.distance_to_lnglat_meters(0.005, 0.01)).to be_within(0.001).of(expected)
|
|
128
|
+
expect(TG::Geometry.point(10, 90).distance_to_lnglat_meters(0, 90)).to be_finite
|
|
129
|
+
expect(TG::Geometry.point(10, 90).nearest_point_lnglat(0, 90)).to eq([10.0, 90.0])
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
it "keeps Geom and Index memsize unchanged after distance calls" do
|
|
133
|
+
require "objspace"
|
|
134
|
+
|
|
135
|
+
geom = TG::Geometry.polygon([[0, 0], [10, 0], [10, 10], [0, 10], [0, 0]])
|
|
136
|
+
index = TG::Geometry::Index.build([[:zone, geom]], via: :geom, strategy: :rtree)
|
|
137
|
+
geom_size = ObjectSpace.memsize_of(geom)
|
|
138
|
+
index_size = ObjectSpace.memsize_of(index)
|
|
139
|
+
|
|
140
|
+
5.times do
|
|
141
|
+
geom.distance_to_xy(5, 5)
|
|
142
|
+
geom.nearest_point_lnglat(0.01, 0.01)
|
|
143
|
+
index.within_distance_xy(5, 5, 10)
|
|
144
|
+
index.within_distance_lnglat_meters(0.01, 0.01, 2_000)
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
expect(ObjectSpace.memsize_of(geom)).to eq(geom_size)
|
|
148
|
+
expect(ObjectSpace.memsize_of(index)).to eq(index_size)
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
it "survives GC stress and compaction" do
|
|
152
|
+
geom = TG::Geometry.line_string([[0, 0], [10, 0]])
|
|
153
|
+
|
|
154
|
+
GC.stress = true
|
|
155
|
+
expect(geom.distance_to_xy(5, 3)).to be_within(1e-12).of(3.0)
|
|
156
|
+
expect(geom.nearest_point_xy(5, 3)).to eq([5.0, 0.0])
|
|
157
|
+
ensure
|
|
158
|
+
GC.stress = false
|
|
159
|
+
GC.compact if GC.respond_to?(:compact)
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
RSpec.describe "Index distance radius queries" do
|
|
164
|
+
def build_distance_entries
|
|
165
|
+
[
|
|
166
|
+
[:zone, TG::Geometry.polygon([[0, 0], [2, 0], [2, 2], [0, 2], [0, 0]])],
|
|
167
|
+
[:line, TG::Geometry.line_string([[4, 0], [4, 4]])],
|
|
168
|
+
[:point, TG::Geometry.point(8, 0)]
|
|
169
|
+
]
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
it "matches brute force membership for xy and returns filter distances" do
|
|
173
|
+
entries = build_distance_entries
|
|
174
|
+
index = TG::Geometry::Index.build(entries, via: :geom, strategy: :rtree)
|
|
175
|
+
|
|
176
|
+
srand(1234)
|
|
177
|
+
30.times do
|
|
178
|
+
x = rand * 10.0 - 1.0
|
|
179
|
+
y = rand * 5.0 - 1.0
|
|
180
|
+
radius = rand * 3.0
|
|
181
|
+
expected = entries.filter_map do |id, geom|
|
|
182
|
+
distance = geom.distance_to_xy(x, y)
|
|
183
|
+
[id, distance] if distance <= radius
|
|
184
|
+
end
|
|
185
|
+
actual = index.within_distance_xy(x, y, radius)
|
|
186
|
+
|
|
187
|
+
expect(actual.map(&:first)).to eq(expected.map(&:first))
|
|
188
|
+
actual.each do |id, distance|
|
|
189
|
+
geom = entries.assoc(id)[1]
|
|
190
|
+
expect(distance).to be_within(1e-12).of(geom.distance_to_xy(x, y))
|
|
191
|
+
end
|
|
192
|
+
expect(index.within_distance_ids_xy(x, y, radius)).to eq(expected.map(&:first))
|
|
193
|
+
end
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
it "matches brute force membership for lng/lat meters" do
|
|
197
|
+
entries = [
|
|
198
|
+
[:zone, TG::Geometry.polygon([[0, 0], [0.02, 0], [0.02, 0.02], [0, 0.02], [0, 0]])],
|
|
199
|
+
[:line, TG::Geometry.line_string([[0.04, 0], [0.04, 0.04]])],
|
|
200
|
+
[:point, TG::Geometry.point(0.08, 0)]
|
|
201
|
+
]
|
|
202
|
+
index = TG::Geometry::Index.build(entries, via: :geom, strategy: :rtree)
|
|
203
|
+
|
|
204
|
+
srand(5678)
|
|
205
|
+
30.times do
|
|
206
|
+
lng = rand * 0.1 - 0.01
|
|
207
|
+
lat = rand * 0.05 - 0.01
|
|
208
|
+
radius = rand * 3_000.0
|
|
209
|
+
expected = entries.filter_map do |id, geom|
|
|
210
|
+
distance = geom.distance_to_lnglat_meters(lng, lat)
|
|
211
|
+
[id, distance] if distance <= radius
|
|
212
|
+
end
|
|
213
|
+
actual = index.within_distance_lnglat_meters(lng, lat, radius)
|
|
214
|
+
|
|
215
|
+
expect(actual.map(&:first)).to eq(expected.map(&:first))
|
|
216
|
+
actual.each do |id, distance|
|
|
217
|
+
geom = entries.assoc(id)[1]
|
|
218
|
+
expect(distance).to be_within(1e-9).of(geom.distance_to_lnglat_meters(lng, lat))
|
|
219
|
+
end
|
|
220
|
+
expect(index.within_distance_ids_lnglat_meters(lng, lat, radius)).to eq(expected.map(&:first))
|
|
221
|
+
end
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
it "sorts filtered pairs by distance only when requested" do
|
|
225
|
+
entries = build_distance_entries
|
|
226
|
+
index = TG::Geometry::Index.build(entries, via: :geom, strategy: :rtree)
|
|
227
|
+
sorted = index.within_distance_xy(3, 1, 10, sort: true)
|
|
228
|
+
|
|
229
|
+
expect(sorted.map(&:last)).to eq(sorted.map(&:last).sort)
|
|
230
|
+
expect(index.within_distance_xy(3, 1, 10, sort: false).map(&:first)).to eq([:zone, :line, :point])
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
it "handles zero radius and near-pole/full-longitude prefilter cases" do
|
|
234
|
+
zone = TG::Geometry.polygon([[0, 0], [1, 0], [1, 1], [0, 1], [0, 0]])
|
|
235
|
+
pole_point = TG::Geometry.point(90, 90)
|
|
236
|
+
index = TG::Geometry::Index.build([[:zone, zone], [:pole, pole_point]], via: :geom, strategy: :rtree)
|
|
237
|
+
|
|
238
|
+
expect(index.within_distance_ids_xy(0.5, 0.5, 0)).to eq([:zone])
|
|
239
|
+
expect(index.within_distance_ids_lnglat_meters(0, 90, 1)).to eq([:pole])
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
it "rejects invalid arguments and keywords" do
|
|
243
|
+
index = TG::Geometry::Index.build(build_distance_entries, via: :geom, strategy: :rtree)
|
|
244
|
+
geom = TG::Geometry.point(0, 0)
|
|
245
|
+
|
|
246
|
+
expect { geom.distance_to_lnglat_meters(Float::NAN, 0) }.to raise_error(TG::Geometry::ArgumentError)
|
|
247
|
+
expect { geom.distance_to_lnglat_meters(0, Float::INFINITY) }.to raise_error(TG::Geometry::ArgumentError)
|
|
248
|
+
expect { geom.distance_to_lnglat_meters(181, 0) }.to raise_error(TG::Geometry::ArgumentError)
|
|
249
|
+
expect { geom.distance_to_lnglat_meters(0, -91) }.to raise_error(TG::Geometry::ArgumentError)
|
|
250
|
+
expect { geom.distance_to_xy(0, Float::NAN) }.to raise_error(TG::Geometry::ArgumentError)
|
|
251
|
+
expect { geom.distance_to_xy(0, 0, metric: :meters) }.to raise_error(TG::Geometry::ArgumentError)
|
|
252
|
+
expect { index.within_distance_xy(0, 0, -1) }.to raise_error(TG::Geometry::ArgumentError)
|
|
253
|
+
expect { index.within_distance_lnglat_meters(0, 0, Float::INFINITY) }.to raise_error(TG::Geometry::ArgumentError)
|
|
254
|
+
expect { index.within_distance_xy(0, 0, 1, bogus: true) }.to raise_error(TG::Geometry::ArgumentError)
|
|
255
|
+
expect { index.within_distance_ids_xy(0, 0, 1, sort: true) }.to raise_error(TG::Geometry::ArgumentError)
|
|
256
|
+
expect { index.within_distance_ids_lnglat_meters(0, 0, 1, sort: true) }.to raise_error(TG::Geometry::ArgumentError)
|
|
257
|
+
end
|
|
258
|
+
end
|
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.3.
|
|
4
|
+
version: 0.3.2
|
|
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-
|
|
11
|
+
date: 2026-06-07 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: rake
|
|
@@ -64,9 +64,9 @@ description: Defines TG::Geometry with immutable Geom parsing and constructor wr
|
|
|
64
64
|
wrappers, value Segment wrappers, Registry reload sugar, optional ActiveRecord source
|
|
65
65
|
helpers, and an immutable geofencing-oriented Index with owned and borrowed geometry
|
|
66
66
|
ingestion, flat/rtree strategies, deterministic ordered id results, exact rtree
|
|
67
|
-
allocation accounting, and native-endian packed point batch queries,
|
|
68
|
-
|
|
69
|
-
support is not claimed.
|
|
67
|
+
allocation accounting, and native-endian packed point batch queries, explicit local
|
|
68
|
+
point-to-geometry distance/radius queries, and FeatureSource GeoJSON FeatureCollection
|
|
69
|
+
extraction/build paths over vendored C sources. Ractor support is not claimed.
|
|
70
70
|
email:
|
|
71
71
|
- romnhajdarov@gmail.com
|
|
72
72
|
executables: []
|
|
@@ -81,6 +81,9 @@ files:
|
|
|
81
81
|
- Rakefile
|
|
82
82
|
- benchmark/_support.rb
|
|
83
83
|
- benchmark/batch_packed_vs_loop.rb
|
|
84
|
+
- benchmark/distance_memory_accounting.rb
|
|
85
|
+
- benchmark/distance_point_geom.rb
|
|
86
|
+
- benchmark/distance_within_radius.rb
|
|
84
87
|
- benchmark/ewkb_roundtrip.rb
|
|
85
88
|
- benchmark/falcon_concurrency.rb
|
|
86
89
|
- benchmark/feature_source.rb
|
|
@@ -135,6 +138,7 @@ files:
|
|
|
135
138
|
- spec/batch_packed_spec.rb
|
|
136
139
|
- spec/concurrency_spec.rb
|
|
137
140
|
- spec/constructors_spec.rb
|
|
141
|
+
- spec/distance_spec.rb
|
|
138
142
|
- spec/error_hardening_spec.rb
|
|
139
143
|
- spec/feature_source_nogvl_spec.rb
|
|
140
144
|
- spec/feature_source_spec.rb
|
|
@@ -202,5 +206,6 @@ rubygems_version: 3.3.27
|
|
|
202
206
|
signing_key:
|
|
203
207
|
specification_version: 4
|
|
204
208
|
summary: Native extension for TG::Geometry parsing, predicates, immutable indexes,
|
|
205
|
-
FeatureSource imports, registries, low-level wrappers,
|
|
209
|
+
FeatureSource imports, registries, low-level wrappers, packed point batches, and
|
|
210
|
+
local distance queries
|
|
206
211
|
test_files: []
|