tg_geometry 0.2.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +37 -0
- data/README.md +107 -14
- data/benchmark/ewkb_roundtrip.rb +29 -0
- data/benchmark/geom_query.rb +33 -0
- data/benchmark/nearest_segment.rb +20 -0
- data/docs/GEOMETRY_QUERIES.md +23 -0
- data/docs/LIMITATIONS.md +12 -10
- data/docs/NEAREST_SEGMENT.md +17 -0
- data/docs/SRID_AND_EWKB.md +23 -0
- data/ext/tg_geometry/tg_geometry_ext.c +1176 -4
- data/lib/tg/geometry/active_record_source.rb +16 -34
- data/lib/tg/geometry/active_record_type.rb +61 -0
- data/lib/tg/geometry/registry.rb +17 -68
- data/lib/tg/geometry/version.rb +1 -1
- data/lib/tg/geometry.rb +85 -0
- data/spec/active_record_type_spec.rb +45 -0
- data/spec/constructors_spec.rb +104 -0
- data/spec/fixtures/feature_source/invalid_geometry_middle.geojson +8 -0
- data/spec/fixtures/feature_source/malformed_json.geojson +1 -0
- data/spec/fixtures/feature_source/mixed_geometry_types.geojson +8 -0
- data/spec/fixtures/feature_source/osm_like_feature_collection.geojson +10 -0
- data/spec/fixtures/feature_source/properties_null_missing.geojson +7 -0
- data/spec/fixtures/feature_source/simple_feature_collection.geojson +15 -0
- data/spec/fixtures/postgis/README.md +16 -0
- data/spec/fixtures/postgis/boundary_point_cases.geojson +83 -0
- data/spec/fixtures/postgis/multipolygon_large.ewkb +0 -0
- data/spec/fixtures/postgis/point_4326.ewkb +0 -0
- data/spec/fixtures/postgis/polygon_3857.ewkb +0 -0
- data/spec/fixtures/postgis/polygon_4326_simple.ewkb +0 -0
- data/spec/fixtures/postgis/polygon_4326_with_hole.ewkb +0 -0
- data/spec/index_geom_query_spec.rb +68 -0
- data/spec/keyword_validation_spec.rb +31 -0
- data/spec/nearest_segment_spec.rb +62 -0
- data/spec/postgis_fixtures_spec.rb +68 -0
- data/spec/srid_spec.rb +43 -0
- data/spec/to_ewkb_spec.rb +37 -0
- metadata +50 -2
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "spec_helper"
|
|
4
|
+
|
|
5
|
+
RSpec.describe "Index geometry queries" do
|
|
6
|
+
def polygon(min_x, min_y, max_x, max_y)
|
|
7
|
+
TG::Geometry.polygon([[min_x, min_y], [max_x, min_y], [max_x, max_y], [min_x, max_y], [min_x, min_y]])
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
let(:zones) do
|
|
11
|
+
[
|
|
12
|
+
[:a, polygon(0, 0, 10, 10)],
|
|
13
|
+
[:b, polygon(8, 8, 18, 18)],
|
|
14
|
+
[:c, polygon(30, 30, 40, 40)]
|
|
15
|
+
]
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def build(strategy)
|
|
19
|
+
TG::Geometry::Index.build(zones, via: :geom, strategy: strategy)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
it "returns intersecting ids in insertion order" do
|
|
23
|
+
query = polygon(9, 9, 12, 12)
|
|
24
|
+
|
|
25
|
+
expect(build(:flat).intersecting_geom_ids(query)).to eq(%i[a b])
|
|
26
|
+
expect(build(:rtree).intersecting_geom_ids(query)).to eq(%i[a b])
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
it "supports line query intersections" do
|
|
30
|
+
query = TG::Geometry.line_string([[35, 35], [45, 45]])
|
|
31
|
+
|
|
32
|
+
expect(build(:flat).intersecting_geom_ids(query)).to eq([:c])
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
it "returns empty arrays for outside query geometries" do
|
|
36
|
+
expect(build(:rtree).intersecting_geom_ids(polygon(100, 100, 101, 101))).to eq([])
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
it "uses stored_geom covers query semantics" do
|
|
40
|
+
index = build(:flat)
|
|
41
|
+
|
|
42
|
+
expect(index.covering_geom_ids(TG::Geometry.point(5, 5))).to eq([:a])
|
|
43
|
+
expect(index.covering_geom_ids(TG::Geometry.point(0, 5))).to eq([:a])
|
|
44
|
+
expect(index.covering_geom_ids(polygon(9, 9, 12, 12))).to eq([:b])
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
it "uses stored_geom contains query semantics and excludes boundary" do
|
|
48
|
+
index = build(:flat)
|
|
49
|
+
|
|
50
|
+
expect(index.containing_geom_ids(TG::Geometry.point(5, 5))).to eq([:a])
|
|
51
|
+
expect(index.containing_geom_ids(TG::Geometry.point(0, 5))).to eq([])
|
|
52
|
+
expect(index.covering_geom_ids(TG::Geometry.point(0, 5))).to eq([:a])
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
it "does not read Index.build predicate for geometry methods" do
|
|
56
|
+
index = TG::Geometry::Index.build(zones, via: :geom, strategy: :flat, predicate: :contains)
|
|
57
|
+
|
|
58
|
+
expect(index.covering_geom_ids(TG::Geometry.point(0, 5))).to eq([:a])
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
it "raises TypeError for non-Geom query values" do
|
|
62
|
+
index = build(:flat)
|
|
63
|
+
|
|
64
|
+
expect { index.intersecting_geom_ids(nil) }.to raise_error(TypeError)
|
|
65
|
+
expect { index.covering_geom_ids("bad") }.to raise_error(TypeError)
|
|
66
|
+
expect { index.containing_geom_ids(1) }.to raise_error(TypeError)
|
|
67
|
+
end
|
|
68
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "spec_helper"
|
|
4
|
+
|
|
5
|
+
RSpec.describe "strict keyword validation" do
|
|
6
|
+
let(:geom_without_srid) { TG::Geometry.parse_wkt("POINT (1 2)") }
|
|
7
|
+
let(:wkb) { geom_without_srid.to_wkb }
|
|
8
|
+
|
|
9
|
+
it "rejects unsupported SRID and EWKB flags on parse_wkb" do
|
|
10
|
+
expect { TG::Geometry.parse_wkb(wkb, srid: 4326) }
|
|
11
|
+
.to raise_error(TG::Geometry::ArgumentError, /unknown keyword: :srid/)
|
|
12
|
+
expect { TG::Geometry.parse_wkb(wkb, ewkb: true) }
|
|
13
|
+
.to raise_error(TG::Geometry::ArgumentError, /unknown keyword: :ewkb/)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
it "rejects public release_gvl knobs on new v0.3.0 APIs" do
|
|
17
|
+
expect { TG::Geometry.line_string([[0, 0], [1, 1]], release_gvl: true) }
|
|
18
|
+
.to raise_error(TG::Geometry::ArgumentError, /unknown keyword: :release_gvl/)
|
|
19
|
+
expect { geom_without_srid.to_ewkb(srid: 4326, release_gvl: true) }
|
|
20
|
+
.to raise_error(TG::Geometry::ArgumentError, /unknown keyword: :release_gvl/)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
it "rejects unknown constructor keywords" do
|
|
24
|
+
square = [[0, 0], [1, 0], [1, 1], [0, 0]]
|
|
25
|
+
|
|
26
|
+
expect { TG::Geometry.polygon(square, autoclose: true) }
|
|
27
|
+
.to raise_error(TG::Geometry::ArgumentError, /unknown keyword: :autoclose/)
|
|
28
|
+
expect { TG::Geometry.multi_polygon([{ exterior: square, holes: [], extra: true }]) }
|
|
29
|
+
.to raise_error(TG::Geometry::ArgumentError, /unknown keyword: :extra/)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "spec_helper"
|
|
4
|
+
|
|
5
|
+
RSpec.describe "nearest_segment" do
|
|
6
|
+
it "finds nearest line segments and projection points" do
|
|
7
|
+
line = TG::Geometry.line_string([[0, 0], [10, 0], [10, 10]]).line
|
|
8
|
+
nearest = line.nearest_segment(5, 3)
|
|
9
|
+
|
|
10
|
+
expect(nearest).to be_a(TG::Geometry::NearestSegment)
|
|
11
|
+
expect(nearest).to be_frozen
|
|
12
|
+
expect(nearest.segment).to be_a(TG::Geometry::Segment)
|
|
13
|
+
expect(nearest.index).to be >= 0
|
|
14
|
+
expect(nearest.distance).to be_within(1e-12).of(3.0)
|
|
15
|
+
expect(nearest.point).to eq([5.0, 0.0])
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
it "returns zero distance for a point on a segment" do
|
|
19
|
+
nearest = TG::Geometry.line_string([[0, 0], [10, 0]]).line.nearest_segment(5, 0)
|
|
20
|
+
|
|
21
|
+
expect(nearest.index).to eq(0)
|
|
22
|
+
expect(nearest.distance).to be_within(1e-12).of(0.0)
|
|
23
|
+
expect(nearest.point).to eq([5.0, 0.0])
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
it "handles outside-bbox and degenerate segments" do
|
|
27
|
+
outside = TG::Geometry.line_string([[0, 0], [10, 0]]).line.nearest_segment(20, 5)
|
|
28
|
+
degenerate = TG::Geometry.line_string([[1, 1], [1, 1]]).line.nearest_segment(4, 5)
|
|
29
|
+
|
|
30
|
+
expect(outside.index).to eq(0)
|
|
31
|
+
expect(outside.point).to eq([10.0, 0.0])
|
|
32
|
+
expect(degenerate.index).to eq(0)
|
|
33
|
+
expect(degenerate.point).to eq([1.0, 1.0])
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
it "finds nearest ring segments without depending on equal-distance tie break" do
|
|
37
|
+
ring = TG::Geometry.polygon([[0, 0], [10, 0], [10, 10], [0, 10], [0, 0]]).polygon.exterior_ring
|
|
38
|
+
center = ring.nearest_segment(5, 5)
|
|
39
|
+
corner = ring.nearest_segment(0, 0)
|
|
40
|
+
|
|
41
|
+
expect(center.index).to be_between(0, 3).inclusive
|
|
42
|
+
expect(center.distance).to be_within(1e-12).of(5.0)
|
|
43
|
+
expect(corner.distance).to be_within(1e-12).of(0.0)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
it "keeps nearest result valid after parent GC" do
|
|
47
|
+
nearest = TG::Geometry.line_string([[0, 0], [10, 0]]).line.nearest_segment(5, 2)
|
|
48
|
+
|
|
49
|
+
GC.start
|
|
50
|
+
GC.compact if GC.respond_to?(:compact)
|
|
51
|
+
|
|
52
|
+
expect(nearest.segment.points).to eq([[0.0, 0.0], [10.0, 0.0]])
|
|
53
|
+
expect(nearest.point).to eq([5.0, 0.0])
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
it "rejects non-finite coordinates" do
|
|
57
|
+
line = TG::Geometry.line_string([[0, 0], [10, 0]]).line
|
|
58
|
+
|
|
59
|
+
expect { line.nearest_segment(Float::NAN, 0) }.to raise_error(TG::Geometry::ArgumentError)
|
|
60
|
+
expect { line.nearest_segment(0, Float::INFINITY) }.to raise_error(TG::Geometry::ArgumentError)
|
|
61
|
+
end
|
|
62
|
+
end
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
require "spec_helper"
|
|
5
|
+
|
|
6
|
+
RSpec.describe "PostGIS EWKB fixtures" do
|
|
7
|
+
FIXTURE_ROOT = File.expand_path("fixtures/postgis", __dir__)
|
|
8
|
+
|
|
9
|
+
CASES = {
|
|
10
|
+
"point_4326.ewkb" => { srid: 4326, bbox: [37.6, 55.7, 37.6, 55.7] },
|
|
11
|
+
"polygon_4326_simple.ewkb" => { srid: 4326, bbox: [0.0, 0.0, 10.0, 10.0] },
|
|
12
|
+
"polygon_4326_with_hole.ewkb" => { srid: 4326, bbox: [0.0, 0.0, 10.0, 10.0] },
|
|
13
|
+
"polygon_3857.ewkb" => { srid: 3857, bbox: [0.0, 0.0, 10.0, 10.0] },
|
|
14
|
+
"multipolygon_large.ewkb" => { srid: 4326, bbox: [99.0, 49.0, 101.0, 51.0] }
|
|
15
|
+
}.freeze
|
|
16
|
+
|
|
17
|
+
CASES.each do |filename, expected|
|
|
18
|
+
it "parses and roundtrips #{filename}" do
|
|
19
|
+
bytes = File.binread(File.join(FIXTURE_ROOT, filename))
|
|
20
|
+
geom = TG::Geometry.parse_wkb(bytes)
|
|
21
|
+
bbox = geom.bbox
|
|
22
|
+
|
|
23
|
+
expect(geom.srid).to eq(expected[:srid])
|
|
24
|
+
expect([bbox.min_x, bbox.min_y, bbox.max_x, bbox.max_y]).to all(be_a(Float))
|
|
25
|
+
[bbox.min_x, bbox.min_y, bbox.max_x, bbox.max_y].zip(expected[:bbox]).each do |actual, expected_coord|
|
|
26
|
+
expect(actual).to be_within(1e-9).of(expected_coord)
|
|
27
|
+
end
|
|
28
|
+
expect(geom.to_ewkb).to eq(bytes)
|
|
29
|
+
expect(TG::Geometry.parse_wkb(geom.to_wkb).srid).to be_nil
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
it "keeps hole coverage semantics stable" do
|
|
34
|
+
geom = TG::Geometry.parse_wkb(File.binread(File.join(FIXTURE_ROOT, "polygon_4326_with_hole.ewkb")))
|
|
35
|
+
|
|
36
|
+
expect(geom.covers_xy?(3, 3)).to be(false)
|
|
37
|
+
expect(geom.covers_xy?(1, 1)).to be(true)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
it "checks boundary cases from GeoJSON" do
|
|
41
|
+
simple = TG::Geometry.parse_wkb(File.binread(File.join(FIXTURE_ROOT, "polygon_4326_simple.ewkb")))
|
|
42
|
+
data = JSON.parse(File.read(File.join(FIXTURE_ROOT, "boundary_point_cases.geojson")))
|
|
43
|
+
points = data.fetch("features").to_h do |feature|
|
|
44
|
+
[feature.fetch("properties").fetch("name"), feature.fetch("geometry").fetch("coordinates")]
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
expect(simple.covers_xy?(*points.fetch("inside_simple"))).to be(true)
|
|
48
|
+
expect(simple.covers_xy?(*points.fetch("boundary_simple"))).to be(true)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
it "sanity-checks the large multipolygon quickly" do
|
|
52
|
+
started = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
53
|
+
geom = TG::Geometry.parse_wkb(File.binread(File.join(FIXTURE_ROOT, "multipolygon_large.ewkb")))
|
|
54
|
+
elapsed = Process.clock_gettime(Process::CLOCK_MONOTONIC) - started
|
|
55
|
+
|
|
56
|
+
expect(elapsed).to be < 1.0
|
|
57
|
+
expect(geom.covers_xy?(100.0, 50.0)).to be(true)
|
|
58
|
+
expect(geom.covers_xy?(200.0, 50.0)).to be(false)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
it "documents SRID mismatch as user responsibility" do
|
|
62
|
+
geom_4326 = TG::Geometry.parse_wkb(File.binread(File.join(FIXTURE_ROOT, "polygon_4326_simple.ewkb")))
|
|
63
|
+
geom_3857 = TG::Geometry.parse_wkb(File.binread(File.join(FIXTURE_ROOT, "polygon_3857.ewkb")))
|
|
64
|
+
|
|
65
|
+
expect(geom_4326.srid).not_to eq(geom_3857.srid)
|
|
66
|
+
expect(geom_4326.intersects?(geom_3857)).to be(true)
|
|
67
|
+
end
|
|
68
|
+
end
|
data/spec/srid_spec.rb
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "spec_helper"
|
|
4
|
+
|
|
5
|
+
RSpec.describe "SRID metadata" do
|
|
6
|
+
let(:plain_wkb) { TG::Geometry.parse_wkt("POINT (1 2)").to_wkb }
|
|
7
|
+
let(:ewkb_4326) { TG::Geometry.parse_wkt("POINT (1 2)").to_ewkb(srid: 4326) }
|
|
8
|
+
let(:ewkb_3857) { TG::Geometry.parse_wkt("POINT (1 2)").to_ewkb(srid: 3857) }
|
|
9
|
+
let(:ewkb_0) { TG::Geometry.parse_wkt("POINT (1 2)").to_ewkb(srid: 0) }
|
|
10
|
+
|
|
11
|
+
it "returns nil for plain WKB and non-SRID text formats" do
|
|
12
|
+
expect(TG::Geometry.parse_wkb(plain_wkb).srid).to be_nil
|
|
13
|
+
expect(TG::Geometry.parse_geojson('{"type":"Point","coordinates":[1,2]}').srid).to be_nil
|
|
14
|
+
expect(TG::Geometry.parse_wkt("POINT (1 2)").srid).to be_nil
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
it "extracts SRID from little-endian EWKB and hex EWKB" do
|
|
18
|
+
geom = TG::Geometry.parse_wkb(ewkb_4326)
|
|
19
|
+
|
|
20
|
+
expect(geom.srid).to eq(4326)
|
|
21
|
+
expect([geom.bbox.min_x, geom.bbox.min_y]).to eq([1.0, 2.0])
|
|
22
|
+
expect(TG::Geometry.parse_wkb(ewkb_3857).srid).to eq(3857)
|
|
23
|
+
expect(TG::Geometry.parse_wkb(ewkb_0).srid).to eq(0)
|
|
24
|
+
expect(TG::Geometry.parse_hex(ewkb_4326.unpack1("H*")).srid).to eq(4326)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
it "extracts SRID from big-endian EWKB" do
|
|
28
|
+
# Big-endian EWKB: byte order 0, type POINT | SRID flag, SRID 4326, x=1.0, y=2.0.
|
|
29
|
+
ewkb = [0, 0x20000001, 4326].pack("C N N") + [1.0, 2.0].pack("G G")
|
|
30
|
+
|
|
31
|
+
expect(TG::Geometry.parse_wkb(ewkb).srid).to eq(4326)
|
|
32
|
+
expect(TG::Geometry.parse_wkb(ewkb).point).to eq([1.0, 2.0])
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
it "does not crash on malformed EWKB" do
|
|
36
|
+
expect { TG::Geometry.parse_wkb("\x01\x01\x00".b) }.to raise_error(TG::Geometry::ParseError)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
it "uses constructor srid metadata" do
|
|
40
|
+
expect(TG::Geometry.polygon([[0, 0], [1, 0], [1, 1], [0, 0]], srid: 4326).srid).to eq(4326)
|
|
41
|
+
expect(TG::Geometry.polygon([[0, 0], [1, 0], [1, 1], [0, 0]]).srid).to be_nil
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "spec_helper"
|
|
4
|
+
|
|
5
|
+
RSpec.describe "Geom#to_ewkb" do
|
|
6
|
+
let(:geom_without_srid) { TG::Geometry.parse_wkt("POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))") }
|
|
7
|
+
let(:geom_with_srid) { TG::Geometry.parse_wkb(geom_without_srid.to_ewkb(srid: 4326)) }
|
|
8
|
+
|
|
9
|
+
it "roundtrips semantic geometry and SRID" do
|
|
10
|
+
ewkb = geom_with_srid.to_ewkb
|
|
11
|
+
reparsed = TG::Geometry.parse_wkb(ewkb)
|
|
12
|
+
|
|
13
|
+
expect(reparsed.srid).to eq(4326)
|
|
14
|
+
expect(reparsed.type).to eq(:polygon)
|
|
15
|
+
expect(reparsed.bbox.max_x).to eq(10.0)
|
|
16
|
+
expect(reparsed.covers_xy?(5, 5)).to be(true)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
it "can add or override SRID explicitly" do
|
|
20
|
+
expect(TG::Geometry.parse_wkb(geom_without_srid.to_ewkb(srid: 4326)).srid).to eq(4326)
|
|
21
|
+
expect(TG::Geometry.parse_wkb(geom_with_srid.to_ewkb(srid: 3857)).srid).to eq(3857)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
it "returns frozen binary strings" do
|
|
25
|
+
ewkb = geom_with_srid.to_ewkb
|
|
26
|
+
|
|
27
|
+
expect(ewkb.encoding).to eq(Encoding::BINARY)
|
|
28
|
+
expect(ewkb).to be_frozen
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
it "rejects missing or invalid SRID" do
|
|
32
|
+
expect { geom_without_srid.to_ewkb }.to raise_error(TG::Geometry::ArgumentError)
|
|
33
|
+
expect { geom_with_srid.to_ewkb(srid: -1) }.to raise_error(TG::Geometry::ArgumentError)
|
|
34
|
+
expect { geom_with_srid.to_ewkb(srid: 2**31) }.to raise_error(TG::Geometry::ArgumentError)
|
|
35
|
+
expect { geom_with_srid.to_ewkb(srid: "4326") }.to raise_error(TG::Geometry::ArgumentError)
|
|
36
|
+
end
|
|
37
|
+
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.
|
|
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-
|
|
11
|
+
date: 2026-05-27 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: rake
|
|
@@ -38,6 +38,26 @@ 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
|
|
@@ -61,10 +81,13 @@ files:
|
|
|
61
81
|
- Rakefile
|
|
62
82
|
- benchmark/_support.rb
|
|
63
83
|
- benchmark/batch_packed_vs_loop.rb
|
|
84
|
+
- benchmark/ewkb_roundtrip.rb
|
|
64
85
|
- benchmark/falcon_concurrency.rb
|
|
65
86
|
- benchmark/feature_source.rb
|
|
66
87
|
- benchmark/flat_vs_rtree.rb
|
|
88
|
+
- benchmark/geom_query.rb
|
|
67
89
|
- benchmark/gvl_threshold.rb
|
|
90
|
+
- benchmark/nearest_segment.rb
|
|
68
91
|
- benchmark/objectspace_memsize.rb
|
|
69
92
|
- benchmark/parse_throughput.rb
|
|
70
93
|
- benchmark/rss_stability.rb
|
|
@@ -74,8 +97,11 @@ files:
|
|
|
74
97
|
- docs/CONCURRENCY.md
|
|
75
98
|
- docs/ERROR_HANDLING.md
|
|
76
99
|
- docs/FEATURE_SOURCE.md
|
|
100
|
+
- docs/GEOMETRY_QUERIES.md
|
|
77
101
|
- docs/LIMITATIONS.md
|
|
78
102
|
- docs/MEMORY_OWNERSHIP.md
|
|
103
|
+
- docs/NEAREST_SEGMENT.md
|
|
104
|
+
- docs/SRID_AND_EWKB.md
|
|
79
105
|
- ext/tg_geometry/extconf.rb
|
|
80
106
|
- ext/tg_geometry/tg_geometry_ext.c
|
|
81
107
|
- ext/tg_geometry/tg_geometry_vendor_json.c
|
|
@@ -98,17 +124,33 @@ files:
|
|
|
98
124
|
- ext/tg_geometry/vendor/tg/tg.h
|
|
99
125
|
- lib/tg/geometry.rb
|
|
100
126
|
- lib/tg/geometry/active_record_source.rb
|
|
127
|
+
- lib/tg/geometry/active_record_type.rb
|
|
101
128
|
- lib/tg/geometry/registry.rb
|
|
102
129
|
- lib/tg/geometry/version.rb
|
|
103
130
|
- lib/tg_geometry.rb
|
|
104
131
|
- script/vendor_libs.rb
|
|
105
132
|
- spec/active_record_source_spec.rb
|
|
133
|
+
- spec/active_record_type_spec.rb
|
|
106
134
|
- spec/auto_strategy_spec.rb
|
|
107
135
|
- spec/batch_packed_spec.rb
|
|
108
136
|
- spec/concurrency_spec.rb
|
|
137
|
+
- spec/constructors_spec.rb
|
|
109
138
|
- spec/error_hardening_spec.rb
|
|
110
139
|
- spec/feature_source_nogvl_spec.rb
|
|
111
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
|
|
112
154
|
- spec/format_coverage_spec.rb
|
|
113
155
|
- spec/fuzz_spec.rb
|
|
114
156
|
- spec/geom_api_spec.rb
|
|
@@ -116,17 +158,23 @@ files:
|
|
|
116
158
|
- spec/index_borrowed_geometry_spec.rb
|
|
117
159
|
- spec/index_build_spec.rb
|
|
118
160
|
- spec/index_flat_query_spec.rb
|
|
161
|
+
- spec/index_geom_query_spec.rb
|
|
119
162
|
- spec/index_owned_geometry_spec.rb
|
|
120
163
|
- spec/index_rtree_accounting_spec.rb
|
|
121
164
|
- spec/index_rtree_order_spec.rb
|
|
165
|
+
- spec/keyword_validation_spec.rb
|
|
122
166
|
- spec/load_and_errors_spec.rb
|
|
123
167
|
- spec/low_level_geometry_spec.rb
|
|
124
168
|
- spec/memory_gc_spec.rb
|
|
169
|
+
- spec/nearest_segment_spec.rb
|
|
170
|
+
- spec/postgis_fixtures_spec.rb
|
|
125
171
|
- spec/ractor_spec.rb
|
|
126
172
|
- spec/rect_api_spec.rb
|
|
127
173
|
- spec/registry_spec.rb
|
|
128
174
|
- spec/spec_helper.rb
|
|
175
|
+
- spec/srid_spec.rb
|
|
129
176
|
- spec/tg_api_coverage_spec.rb
|
|
177
|
+
- spec/to_ewkb_spec.rb
|
|
130
178
|
- spec/vendor_sources_spec.rb
|
|
131
179
|
homepage: https://github.com/roman-haidarov/tg_geometry
|
|
132
180
|
licenses:
|