ffi-geos 2.3.1 → 2.4.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/.rubocop-minitest.yml +40 -17
- data/.rubocop.yml +145 -47
- data/.rubocop_todo.yml +4 -3
- data/MIT-LICENSE +1 -1
- data/README.rdoc +2 -0
- data/ffi-geos.gemspec +1 -1
- data/lib/ffi-geos/geojson_reader.rb +35 -0
- data/lib/ffi-geos/geojson_writer.rb +49 -0
- data/lib/ffi-geos/geometry.rb +6 -0
- data/lib/ffi-geos/geometry_collection.rb +1 -1
- data/lib/ffi-geos/line_string.rb +1 -1
- data/lib/ffi-geos/polygon.rb +1 -1
- data/lib/ffi-geos/prepared_geometry.rb +20 -0
- data/lib/ffi-geos/version.rb +1 -1
- data/lib/ffi-geos/wkb_writer.rb +12 -0
- data/lib/ffi-geos.rb +78 -0
- data/test/geojson_reader_tests.rb +170 -0
- data/test/geojson_writer_tests.rb +103 -0
- data/test/geometry_tests.rb +18 -0
- data/test/prepared_geometry_tests.rb +30 -0
- data/test/wkb_writer_tests.rb +20 -0
- metadata +10 -4
data/MIT-LICENSE
CHANGED
data/README.rdoc
CHANGED
@@ -54,6 +54,8 @@ Ruby bindings along with the following enhancements and additions:
|
|
54
54
|
GEOS library and perform other work or cancel GEOS calls outright. The
|
55
55
|
interruption API was added in GEOS 3.4.0.
|
56
56
|
|
57
|
+
* Geos::GeoJSONReader and Geos::GeoJSONWriter support on GEOS 3.10+.
|
58
|
+
|
57
59
|
== New Methods and Additions (not exhaustive)
|
58
60
|
|
59
61
|
* SRIDs can be copied on many operations. GEOS doesn't usually copy SRIDs
|
data/ffi-geos.gemspec
CHANGED
@@ -7,7 +7,7 @@ Gem::Specification.new do |s|
|
|
7
7
|
s.version = Geos::VERSION
|
8
8
|
|
9
9
|
s.required_rubygems_version = Gem::Requirement.new('>= 0') if s.respond_to? :required_rubygems_version=
|
10
|
-
s.required_ruby_version = '>= 2.
|
10
|
+
s.required_ruby_version = '>= 2.6'
|
11
11
|
|
12
12
|
s.authors = ['J Smith']
|
13
13
|
s.description = 'An ffi wrapper for GEOS, a C++ port of the Java Topology Suite (JTS).'
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Geos
|
4
|
+
class GeoJSONReader
|
5
|
+
include Geos::Tools
|
6
|
+
|
7
|
+
attr_reader :ptr
|
8
|
+
|
9
|
+
class ParseError < Geos::ParseError
|
10
|
+
end
|
11
|
+
|
12
|
+
def initialize(*args)
|
13
|
+
ptr = if args.first.is_a?(FFI::Pointer)
|
14
|
+
args.first
|
15
|
+
else
|
16
|
+
FFIGeos.GEOSGeoJSONReader_create_r(Geos.current_handle_pointer, *args)
|
17
|
+
end
|
18
|
+
|
19
|
+
@ptr = FFI::AutoPointer.new(
|
20
|
+
ptr,
|
21
|
+
self.class.method(:release)
|
22
|
+
)
|
23
|
+
end
|
24
|
+
|
25
|
+
def read(json, options = {})
|
26
|
+
cast_geometry_ptr(FFIGeos.GEOSGeoJSONReader_readGeometry_r(Geos.current_handle_pointer, ptr, json), srid: options[:srid])
|
27
|
+
rescue Geos::GEOSException => e
|
28
|
+
raise ParseError, e
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.release(ptr) # :nodoc:
|
32
|
+
FFIGeos.GEOSGeoJSONReader_destroy_r(Geos.current_handle_pointer, ptr)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Geos
|
4
|
+
class GeoJSONWriter
|
5
|
+
attr_accessor :indentation
|
6
|
+
attr_reader :ptr
|
7
|
+
|
8
|
+
def initialize(options = {})
|
9
|
+
options = {
|
10
|
+
indentation: -1
|
11
|
+
}.merge(options)
|
12
|
+
|
13
|
+
ptr = FFIGeos.GEOSGeoJSONWriter_create_r(Geos.current_handle_pointer)
|
14
|
+
@ptr = FFI::AutoPointer.new(
|
15
|
+
ptr,
|
16
|
+
self.class.method(:release)
|
17
|
+
)
|
18
|
+
|
19
|
+
set_options(options)
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.release(ptr) # :nodoc:
|
23
|
+
FFIGeos.GEOSGeoJSONWriter_destroy_r(Geos.current_handle_pointer, ptr)
|
24
|
+
end
|
25
|
+
|
26
|
+
def set_options(options) # :nodoc:
|
27
|
+
[:indentation].each do |k|
|
28
|
+
send("#{k}=", options[k]) if respond_to?("#{k}=") && options.key?(k)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
private :set_options
|
32
|
+
|
33
|
+
# Options can be set temporarily for individual writes using an options
|
34
|
+
# Hash. Options include :indentation.
|
35
|
+
def write(geom, options = nil)
|
36
|
+
unless options.nil?
|
37
|
+
old_options = {
|
38
|
+
indentation: indentation
|
39
|
+
}
|
40
|
+
|
41
|
+
set_options(options)
|
42
|
+
end
|
43
|
+
|
44
|
+
FFIGeos.GEOSGeoJSONWriter_writeGeometry_r(Geos.current_handle_pointer, ptr, geom.ptr, indentation)
|
45
|
+
ensure
|
46
|
+
set_options(old_options) unless options.nil?
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
data/lib/ffi-geos/geometry.rb
CHANGED
@@ -637,6 +637,12 @@ module Geos
|
|
637
637
|
end
|
638
638
|
end
|
639
639
|
|
640
|
+
if FFIGeos.respond_to?(:GEOSConstrainedDelaunayTriangulation_r)
|
641
|
+
def constrained_delaunay_triangulation
|
642
|
+
cast_geometry_ptr(FFIGeos.GEOSConstrainedDelaunayTriangulation_r(Geos.current_handle_pointer, ptr))
|
643
|
+
end
|
644
|
+
end
|
645
|
+
|
640
646
|
if FFIGeos.respond_to?(:GEOSVoronoiDiagram_r)
|
641
647
|
# Available in GEOS 3.5.0+
|
642
648
|
#
|
@@ -46,7 +46,7 @@ module Geos
|
|
46
46
|
|
47
47
|
%w{ x y z }.each do |dimension|
|
48
48
|
%w{ max min }.each do |op|
|
49
|
-
native_method = "GEOSGeom_get#{dimension.upcase}#{op[0].upcase}#{op[1
|
49
|
+
native_method = "GEOSGeom_get#{dimension.upcase}#{op[0].upcase}#{op[1..]}_r"
|
50
50
|
|
51
51
|
if FFIGeos.respond_to?(native_method)
|
52
52
|
class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
|
data/lib/ffi-geos/line_string.rb
CHANGED
@@ -146,7 +146,7 @@ module Geos
|
|
146
146
|
|
147
147
|
%w{ max min }.each do |op|
|
148
148
|
%w{ x y }.each do |dimension|
|
149
|
-
native_method = "GEOSGeom_get#{dimension.upcase}#{op[0].upcase}#{op[1
|
149
|
+
native_method = "GEOSGeom_get#{dimension.upcase}#{op[0].upcase}#{op[1..]}_r"
|
150
150
|
|
151
151
|
if FFIGeos.respond_to?(native_method)
|
152
152
|
class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
|
data/lib/ffi-geos/polygon.rb
CHANGED
@@ -78,7 +78,7 @@ module Geos
|
|
78
78
|
|
79
79
|
%w{ max min }.each do |op|
|
80
80
|
%w{ x y }.each do |dimension|
|
81
|
-
native_method = "GEOSGeom_get#{dimension.upcase}#{op[0].upcase}#{op[1
|
81
|
+
native_method = "GEOSGeom_get#{dimension.upcase}#{op[0].upcase}#{op[1..]}_r"
|
82
82
|
|
83
83
|
if FFIGeos.respond_to?(native_method)
|
84
84
|
class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
|
@@ -77,5 +77,25 @@ module Geos
|
|
77
77
|
check_geometry(geom)
|
78
78
|
bool_result(FFIGeos.GEOSPreparedWithin_r(Geos.current_handle_pointer, ptr, geom.ptr))
|
79
79
|
end
|
80
|
+
|
81
|
+
def distance(geom)
|
82
|
+
check_geometry(geom)
|
83
|
+
double_ptr = FFI::MemoryPointer.new(:double)
|
84
|
+
FFIGeos.GEOSPreparedDistance_r(Geos.current_handle_pointer, ptr, geom.ptr, double_ptr)
|
85
|
+
double_ptr.read_double
|
86
|
+
end
|
87
|
+
|
88
|
+
def distance_within?(geom, distance)
|
89
|
+
check_geometry(geom)
|
90
|
+
bool_result(FFIGeos.GEOSPreparedDistanceWithin_r(Geos.current_handle_pointer, ptr, geom.ptr, distance))
|
91
|
+
end
|
92
|
+
|
93
|
+
def nearest_points(geom)
|
94
|
+
check_geometry(geom)
|
95
|
+
|
96
|
+
coord_seq_ptr = FFIGeos.GEOSPreparedNearestPoints_r(Geos.current_handle_pointer, ptr, geom.ptr)
|
97
|
+
|
98
|
+
Geos::CoordinateSequence.new(coord_seq_ptr)
|
99
|
+
end
|
80
100
|
end
|
81
101
|
end
|
data/lib/ffi-geos/version.rb
CHANGED
data/lib/ffi-geos/wkb_writer.rb
CHANGED
@@ -83,6 +83,18 @@ module Geos
|
|
83
83
|
FFIGeos.GEOSWKBWriter_setByteOrder_r(Geos.current_handle_pointer, ptr, val)
|
84
84
|
end
|
85
85
|
|
86
|
+
if FFIGeos.respond_to?(:GEOSWKBWriter_getFlavor_r)
|
87
|
+
def flavor
|
88
|
+
FFIGeos.GEOSWKBWriter_getFlavor_r(Geos.current_handle_pointer, ptr)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
if FFIGeos.respond_to?(:GEOSWKBWriter_setFlavor_r)
|
93
|
+
def flavor=(val)
|
94
|
+
FFIGeos.GEOSWKBWriter_setFlavor_r(Geos.current_handle_pointer, ptr, val)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
86
98
|
private
|
87
99
|
|
88
100
|
def set_options(options) # :nodoc:
|
data/lib/ffi-geos.rb
CHANGED
@@ -15,6 +15,10 @@ module Geos
|
|
15
15
|
File.join(GEOS_BASE, 'wkb_reader')
|
16
16
|
autoload :WkbWriter,
|
17
17
|
File.join(GEOS_BASE, 'wkb_writer')
|
18
|
+
autoload :GeoJSONReader,
|
19
|
+
File.join(GEOS_BASE, 'geojson_reader')
|
20
|
+
autoload :GeoJSONWriter,
|
21
|
+
File.join(GEOS_BASE, 'geojson_writer')
|
18
22
|
autoload :CoordinateSequence,
|
19
23
|
File.join(GEOS_BASE, 'coordinate_sequence')
|
20
24
|
autoload :Geometry,
|
@@ -104,6 +108,11 @@ module Geos
|
|
104
108
|
:ndr, 1 # Little Endian
|
105
109
|
])
|
106
110
|
|
111
|
+
Geos::Flavors = enum(:flavor, [
|
112
|
+
:extended, 1,
|
113
|
+
:iso, 2
|
114
|
+
])
|
115
|
+
|
107
116
|
Geos::BufferCapStyles = enum(:buffer_cap_style, [
|
108
117
|
:round, 1,
|
109
118
|
:flat, 2,
|
@@ -587,6 +596,11 @@ module Geos
|
|
587
596
|
:pointer, :pointer, :pointer, :double, :int
|
588
597
|
],
|
589
598
|
|
599
|
+
GEOSConstrainedDelaunayTriangulation_r: [
|
600
|
+
# *geom, *handle, *geom
|
601
|
+
:pointer, :pointer, :pointer
|
602
|
+
],
|
603
|
+
|
590
604
|
GEOSVoronoiDiagram_r: [
|
591
605
|
# *geom, *handle, *geom, *envelope, tolerance, only_edges
|
592
606
|
:pointer, :pointer, :pointer, :pointer, :double, :int
|
@@ -727,6 +741,11 @@ module Geos
|
|
727
741
|
:int, :pointer, :pointer, :pointer, :pointer
|
728
742
|
],
|
729
743
|
|
744
|
+
GEOSDistanceWithin_r: [
|
745
|
+
# (0 on exception, 1 otherwise), *handle, *geom_a, *geom_b, double distance
|
746
|
+
:char, :pointer, :pointer, :pointer, :double
|
747
|
+
],
|
748
|
+
|
730
749
|
GEOSHausdorffDistance_r: [
|
731
750
|
# (0 on exception, 1 otherwise), *handle, *geom_a, *geom_b, (double *) distance
|
732
751
|
:int, :pointer, :pointer, :pointer, :pointer
|
@@ -955,6 +974,21 @@ module Geos
|
|
955
974
|
# (2 on exception, 1 on true, 0 on false), *handle, *prepared, *geom
|
956
975
|
:char, :pointer, :pointer, :pointer
|
957
976
|
],
|
977
|
+
|
978
|
+
GEOSPreparedDistance_r: [
|
979
|
+
# (1 on success, 0 on failure), *handle, *prepared, *geom, *distance
|
980
|
+
:int, :pointer, :pointer, :pointer, :pointer
|
981
|
+
],
|
982
|
+
|
983
|
+
GEOSPreparedDistanceWithin_r: [
|
984
|
+
# (1 on true, 0 on false), *handle, *prepared, *geom, max_distance
|
985
|
+
:char, :pointer, :pointer, :pointer, :double
|
986
|
+
],
|
987
|
+
|
988
|
+
GEOSPreparedNearestPoints_r: [
|
989
|
+
# *coord_seq, *handle, *prepared, *geom
|
990
|
+
:pointer, :pointer, :pointer, :pointer
|
991
|
+
],
|
958
992
|
#### /PreparedGeometry functions ####
|
959
993
|
|
960
994
|
#### WktReader functions ####
|
@@ -1088,8 +1122,52 @@ module Geos
|
|
1088
1122
|
# void, *handle, *geom, bool
|
1089
1123
|
:void, :pointer, :pointer, :char
|
1090
1124
|
],
|
1125
|
+
|
1126
|
+
GEOSWKBWriter_getFlavor_r: [
|
1127
|
+
# flavor, *handle, *geom
|
1128
|
+
:flavor, :pointer, :pointer
|
1129
|
+
],
|
1130
|
+
|
1131
|
+
GEOSWKBWriter_setFlavor_r: [
|
1132
|
+
# void, *handle, *geom, flavor
|
1133
|
+
:void, :pointer, :pointer, :flavor
|
1134
|
+
],
|
1091
1135
|
#### /WkbWriter functions ####
|
1092
1136
|
|
1137
|
+
#### GeoJSONReader functions ####
|
1138
|
+
GEOSGeoJSONReader_create_r: [
|
1139
|
+
# *geojson_reader, *handle
|
1140
|
+
:pointer, :pointer
|
1141
|
+
],
|
1142
|
+
|
1143
|
+
GEOSGeoJSONReader_readGeometry_r: [
|
1144
|
+
# *geom, *handle, *geojson_reader, string
|
1145
|
+
:pointer, :pointer, :pointer, :string
|
1146
|
+
],
|
1147
|
+
|
1148
|
+
GEOSGeoJSONReader_destroy_r: [
|
1149
|
+
# void, *handle, *geojson_reader
|
1150
|
+
:void, :pointer, :pointer
|
1151
|
+
],
|
1152
|
+
#### /GeoJSONReader functions ###
|
1153
|
+
|
1154
|
+
#### GeoJSONWriter functions ####
|
1155
|
+
GEOSGeoJSONWriter_create_r: [
|
1156
|
+
# *geojson_writer, *handle
|
1157
|
+
:pointer, :pointer
|
1158
|
+
],
|
1159
|
+
|
1160
|
+
GEOSGeoJSONWriter_destroy_r: [
|
1161
|
+
# void, *handle, *geojson_writer
|
1162
|
+
:void, :pointer, :pointer
|
1163
|
+
],
|
1164
|
+
|
1165
|
+
GEOSGeoJSONWriter_writeGeometry_r: [
|
1166
|
+
# string, *handle, *geojson_writer, :geom, :indent
|
1167
|
+
:string, :pointer, :pointer, :pointer, :int
|
1168
|
+
],
|
1169
|
+
#### /GeoJSONWriter functions ####
|
1170
|
+
|
1093
1171
|
#### Linearref functions ####
|
1094
1172
|
GEOSProject_r: [
|
1095
1173
|
# distance, *handle, *geom_a, *geom_b
|
@@ -0,0 +1,170 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'test_helper'
|
4
|
+
|
5
|
+
class GeoJSONReaderTests < Minitest::Test
|
6
|
+
include TestHelper
|
7
|
+
|
8
|
+
attr_reader :json_reader
|
9
|
+
|
10
|
+
def setup
|
11
|
+
super
|
12
|
+
|
13
|
+
skip unless ENV['FORCE_TESTS'] || Geos::FFIGeos.respond_to?(:GEOSGeoJSONReader_create_r)
|
14
|
+
|
15
|
+
@json_reader = Geos::GeoJSONReader.new
|
16
|
+
|
17
|
+
@writer.rounding_precision = 3
|
18
|
+
end
|
19
|
+
|
20
|
+
def json_read(*args, **options)
|
21
|
+
json_reader.read(*args, **options)
|
22
|
+
end
|
23
|
+
|
24
|
+
def geojson_tester(expected, json, **options)
|
25
|
+
assert_equal(expected, write(json_read(json, **options)))
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_point
|
29
|
+
geojson_tester(
|
30
|
+
'POINT (-117.000 33.000)',
|
31
|
+
'{"type":"Point","coordinates":[-117.0,33.0]}'
|
32
|
+
)
|
33
|
+
end
|
34
|
+
|
35
|
+
def test_line_string
|
36
|
+
geojson_tester(
|
37
|
+
'LINESTRING (102.000 0.000, 103.000 1.000, 104.000 0.000, 105.000 1.000)',
|
38
|
+
'{"type":"LineString","coordinates":[[102.0,0.0],[103.0,1.0],[104.0,0.0],[105.0,1.0]]}'
|
39
|
+
)
|
40
|
+
end
|
41
|
+
|
42
|
+
def test_polygon
|
43
|
+
geojson_tester(
|
44
|
+
'POLYGON ((30.000 10.000, 40.000 40.000, 20.000 40.000, 10.000 20.000, 30.000 10.000))',
|
45
|
+
'{"type":"Polygon","coordinates":[[[30,10],[40,40],[20,40],[10,20],[30,10]]]}'
|
46
|
+
)
|
47
|
+
end
|
48
|
+
|
49
|
+
def test_polygon_with_inner_ring
|
50
|
+
geojson_tester(
|
51
|
+
'POLYGON ((35.000 10.000, 45.000 45.000, 15.000 40.000, 10.000 20.000, 35.000 10.000), (20.000 30.000, 35.000 35.000, 30.000 20.000, 20.000 30.000))',
|
52
|
+
'{"type":"Polygon","coordinates":[[[35,10],[45,45],[15,40],[10,20],[35,10]],[[20,30],[35,35],[30,20],[20,30]]]}'
|
53
|
+
)
|
54
|
+
end
|
55
|
+
|
56
|
+
def test_multi_point
|
57
|
+
geojson_tester(
|
58
|
+
'MULTIPOINT (10.000 40.000, 40.000 30.000, 20.000 20.000, 30.000 10.000)',
|
59
|
+
'{"type":"MultiPoint","coordinates":[[10, 40], [40, 30], [20, 20], [30, 10]]}'
|
60
|
+
)
|
61
|
+
end
|
62
|
+
|
63
|
+
def test_multi_line_string
|
64
|
+
geojson_tester(
|
65
|
+
'MULTILINESTRING ((10.000 10.000, 20.000 20.000, 10.000 40.000), (40.000 40.000, 30.000 30.000, 40.000 20.000, 30.000 10.000))',
|
66
|
+
'{"type":"MultiLineString","coordinates":[[[10, 10], [20, 20], [10, 40]],[[40, 40], [30, 30], [40, 20], [30, 10]]]}'
|
67
|
+
)
|
68
|
+
end
|
69
|
+
|
70
|
+
def test_multi_polygon
|
71
|
+
geojson_tester(
|
72
|
+
'MULTIPOLYGON (((40.000 40.000, 20.000 45.000, 45.000 30.000, 40.000 40.000)), ((20.000 35.000, 10.000 30.000, 10.000 10.000, 30.000 5.000, 45.000 20.000, 20.000 35.000), (30.000 20.000, 20.000 15.000, 20.000 25.000, 30.000 20.000)))',
|
73
|
+
'{"type": "MultiPolygon", "coordinates": [[[[40, 40], [20, 45], [45, 30], [40, 40]]], [[[20, 35], [10, 30], [10, 10], [30, 5], [45, 20], [20, 35]], [[30, 20], [20, 15], [20, 25], [30, 20]]]]}'
|
74
|
+
)
|
75
|
+
end
|
76
|
+
|
77
|
+
def test_geometry_collection
|
78
|
+
geojson_tester(
|
79
|
+
'GEOMETRYCOLLECTION (POINT (40.000 10.000), LINESTRING (10.000 10.000, 20.000 20.000, 10.000 40.000), POLYGON ((40.000 40.000, 20.000 45.000, 45.000 30.000, 40.000 40.000)))',
|
80
|
+
'{"type": "GeometryCollection","geometries": [{"type": "Point","coordinates": [40, 10]},{"type": "LineString","coordinates": [[10, 10], [20, 20], [10, 40]]},{"type": "Polygon","coordinates": [[[40, 40], [20, 45], [45, 30], [40, 40]]]}]}'
|
81
|
+
)
|
82
|
+
end
|
83
|
+
|
84
|
+
def test_feature_collection
|
85
|
+
geojson_tester(
|
86
|
+
'GEOMETRYCOLLECTION (POINT (-117.000 33.000), POINT (-122.000 45.000))',
|
87
|
+
'{"type":"FeatureCollection","features":[{"type":"Feature","geometry":{"type":"Point","coordinates":[-117.0,33.0]}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-122.0,45.0]}}]}'
|
88
|
+
)
|
89
|
+
end
|
90
|
+
|
91
|
+
def test_empty_point
|
92
|
+
geojson_tester(
|
93
|
+
'POINT EMPTY',
|
94
|
+
'{"type":"Point","coordinates":[]}'
|
95
|
+
)
|
96
|
+
end
|
97
|
+
|
98
|
+
def test_empty_line_string
|
99
|
+
geojson_tester(
|
100
|
+
'LINESTRING EMPTY',
|
101
|
+
'{"type":"LineString","coordinates":[]}'
|
102
|
+
)
|
103
|
+
end
|
104
|
+
|
105
|
+
def test_empty_polygon
|
106
|
+
geojson_tester(
|
107
|
+
'POLYGON EMPTY',
|
108
|
+
'{"type":"Polygon","coordinates":[]}'
|
109
|
+
)
|
110
|
+
end
|
111
|
+
|
112
|
+
def test_empty_multi_point
|
113
|
+
geojson_tester(
|
114
|
+
'MULTIPOINT EMPTY',
|
115
|
+
'{"type":"MultiPoint","coordinates":[]}'
|
116
|
+
)
|
117
|
+
end
|
118
|
+
|
119
|
+
def test_empty_multi_line_string
|
120
|
+
geojson_tester(
|
121
|
+
'MULTILINESTRING EMPTY',
|
122
|
+
'{"type":"MultiLineString","coordinates":[]}'
|
123
|
+
)
|
124
|
+
end
|
125
|
+
|
126
|
+
def test_empty_multi_polygon
|
127
|
+
geojson_tester(
|
128
|
+
'MULTIPOLYGON EMPTY',
|
129
|
+
'{"type": "MultiPolygon", "coordinates": []}'
|
130
|
+
)
|
131
|
+
end
|
132
|
+
|
133
|
+
def test_empty_geometry_collection
|
134
|
+
geojson_tester(
|
135
|
+
'GEOMETRYCOLLECTION EMPTY',
|
136
|
+
'{"type": "GeometryCollection","geometries": []}'
|
137
|
+
)
|
138
|
+
end
|
139
|
+
|
140
|
+
def test_incomplete_geojson
|
141
|
+
assert_raises(Geos::GeoJSONReader::ParseError) do
|
142
|
+
json_reader.read('{"type":"Point","coordinates":[-117.0]}')
|
143
|
+
end
|
144
|
+
|
145
|
+
assert_raises(Geos::GeoJSONReader::ParseError) do
|
146
|
+
json_reader.read('{"type":"LineString","coordinates":[[1,2],[2]]}')
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
def test_broken_geojson
|
151
|
+
assert_raises(Geos::GeoJSONReader::ParseError) do
|
152
|
+
json_reader.read('<gml>NOT_GEO_JSON</gml>')
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
def test_incompatible_type
|
157
|
+
assert_raises(Geos::GeoJSONReader::ParseError) do
|
158
|
+
json_reader.read('{"type":"Line","coordinates":[[1,2],[2,3]]}')
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
def test_srid_from_options
|
163
|
+
geom = json_reader.read(
|
164
|
+
'{"type":"Point","coordinates":[-117.0,33.0]}',
|
165
|
+
srid: 3857
|
166
|
+
)
|
167
|
+
|
168
|
+
assert_equal(geom.srid, 3857)
|
169
|
+
end
|
170
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'test_helper'
|
4
|
+
|
5
|
+
class GeoJSONWriterTests < Minitest::Test
|
6
|
+
include TestHelper
|
7
|
+
|
8
|
+
attr_reader :json_writer
|
9
|
+
|
10
|
+
def setup
|
11
|
+
super
|
12
|
+
|
13
|
+
skip unless ENV['FORCE_TESTS'] || Geos::FFIGeos.respond_to?(:GEOSGeoJSONWriter_create_r)
|
14
|
+
|
15
|
+
@json_writer = Geos::GeoJSONWriter.new
|
16
|
+
end
|
17
|
+
|
18
|
+
def json_write(*args, **options)
|
19
|
+
json_writer.write(*args, **options)
|
20
|
+
end
|
21
|
+
|
22
|
+
def geojson_tester(expected, geom, **options)
|
23
|
+
assert_equal(expected, json_write(read(geom), **options))
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_point
|
27
|
+
geojson_tester('{"type":"Point","coordinates":[-117.0,33.0]}', 'POINT(-117 33)')
|
28
|
+
end
|
29
|
+
|
30
|
+
def test_line_string
|
31
|
+
geojson_tester('{"type":"LineString","coordinates":[[102.0,0.0],[103.0,1.0],[104.0,0.0],[105.0,1.0]]}', 'LINESTRING(102.0 0.0, 103.0 1.0, 104.0 0.0, 105.0 1.0)')
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_polygon
|
35
|
+
geojson_tester('{"type":"Polygon","coordinates":[[[30.0,10.0],[40.0,40.0],[20.0,40.0],[10.0,20.0],[30.0,10.0]]]}', 'POLYGON((30 10, 40 40, 20 40, 10 20, 30 10))')
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_polygon_with_inner_ring
|
39
|
+
geojson_tester('{"type":"Polygon","coordinates":[[[35.0,10.0],[45.0,45.0],[15.0,40.0],[10.0,20.0],[35.0,10.0]],[[20.0,30.0],[35.0,35.0],[30.0,20.0],[20.0,30.0]]]}', 'POLYGON((35 10, 45 45, 15 40, 10 20, 35 10), (20 30, 35 35, 30 20, 20 30))')
|
40
|
+
end
|
41
|
+
|
42
|
+
def test_multi_point
|
43
|
+
geojson_tester('{"type":"MultiPoint","coordinates":[[10.0,40.0],[40.0,30.0],[20.0,20.0],[30.0,10.0]]}', 'MULTIPOINT ((10 40), (40 30), (20 20), (30 10))')
|
44
|
+
end
|
45
|
+
|
46
|
+
def test_multi_line_string
|
47
|
+
geojson_tester('{"type":"MultiLineString","coordinates":[[[10.0,10.0],[20.0,20.0],[10.0,40.0]],[[40.0,40.0],[30.0,30.0],[40.0,20.0],[30.0,10.0]]]}', 'MULTILINESTRING ((10 10, 20 20, 10 40),(40 40, 30 30, 40 20, 30 10))')
|
48
|
+
end
|
49
|
+
|
50
|
+
def test_multi_polygon
|
51
|
+
geojson_tester('{"type":"MultiPolygon","coordinates":[[[[30.0,20.0],[45.0,40.0],[10.0,40.0],[30.0,20.0]]],[[[15.0,5.0],[40.0,10.0],[10.0,20.0],[5.0,10.0],[15.0,5.0]]]]}', 'MULTIPOLYGON (((30 20, 45 40, 10 40, 30 20)),((15 5, 40 10, 10 20, 5 10, 15 5)))')
|
52
|
+
end
|
53
|
+
|
54
|
+
def test_geometry_collection
|
55
|
+
geojson_tester('{"type":"GeometryCollection","geometries":[{"type":"Point","coordinates":[1.0,1.0]},{"type":"Point","coordinates":[2.0,2.0]}]}', 'GEOMETRYCOLLECTION(POINT(1 1),POINT(2 2))')
|
56
|
+
end
|
57
|
+
|
58
|
+
def test_write_with_indentation
|
59
|
+
geojson_tester(<<~JSON.strip, 'LINESTRING(102.0 0.0, 103.0 1.0, 104.0 0.0, 105.0 1.0)', indentation: 2)
|
60
|
+
{
|
61
|
+
"type": "LineString",
|
62
|
+
"coordinates": [
|
63
|
+
[
|
64
|
+
102.0,
|
65
|
+
0.0
|
66
|
+
],
|
67
|
+
[
|
68
|
+
103.0,
|
69
|
+
1.0
|
70
|
+
],
|
71
|
+
[
|
72
|
+
104.0,
|
73
|
+
0.0
|
74
|
+
],
|
75
|
+
[
|
76
|
+
105.0,
|
77
|
+
1.0
|
78
|
+
]
|
79
|
+
]
|
80
|
+
}
|
81
|
+
JSON
|
82
|
+
end
|
83
|
+
|
84
|
+
def test_empty_point
|
85
|
+
geojson_tester('{"type":"Point","coordinates":[]}', 'POINT EMPTY')
|
86
|
+
end
|
87
|
+
|
88
|
+
def test_empty_line_string
|
89
|
+
geojson_tester('{"type":"LineString","coordinates":[]}', 'LINESTRING EMPTY')
|
90
|
+
end
|
91
|
+
|
92
|
+
def test_empty_polygon
|
93
|
+
geojson_tester('{"type":"Polygon","coordinates":[[]]}', 'POLYGON EMPTY')
|
94
|
+
end
|
95
|
+
|
96
|
+
def test_empty_geometry_collection
|
97
|
+
geojson_tester('{"type":"GeometryCollection","geometries":[]}', 'GEOMETRYCOLLECTION EMPTY')
|
98
|
+
end
|
99
|
+
|
100
|
+
def test_linear_ring
|
101
|
+
geojson_tester('{"type":"LineString","coordinates":[[0.0,0.0],[1.0,1.0],[1.0,0.0],[0.0,0.0]]}', 'LINEARRING (0 0, 1 1, 1 0, 0 0)')
|
102
|
+
end
|
103
|
+
end
|
data/test/geometry_tests.rb
CHANGED
@@ -1845,6 +1845,24 @@ class GeometryTests < Minitest::Test
|
|
1845
1845
|
tester['MULTILINESTRING ((10 0, 10 10), (0 0, 10 10), (0 0, 10 0))', 'MULTIPOINT(0 0, 10 0, 10 10, 11 10)', tolerance: 2.0, only_edges: true]
|
1846
1846
|
end
|
1847
1847
|
|
1848
|
+
def test_constrained_delaunay_triangulation
|
1849
|
+
skip unless ENV['FORCE_TESTS'] || Geos::Geometry.method_defined?(:constrained_delaunay_triangulation)
|
1850
|
+
|
1851
|
+
tester = lambda { |expected, geom|
|
1852
|
+
geom = read(geom)
|
1853
|
+
geom_tri = geom.constrained_delaunay_triangulation
|
1854
|
+
geom_tri.normalize!
|
1855
|
+
|
1856
|
+
assert_equal(write(read(expected).normalize), write(geom_tri))
|
1857
|
+
}
|
1858
|
+
|
1859
|
+
writer.trim = true
|
1860
|
+
|
1861
|
+
tester['GEOMETRYCOLLECTION EMPTY', 'POLYGON EMPTY']
|
1862
|
+
tester['GEOMETRYCOLLECTION EMPTY', 'POINT(0 0)']
|
1863
|
+
tester['GEOMETRYCOLLECTION (POLYGON ((10 10, 20 40, 90 10, 10 10)), POLYGON ((90 90, 20 40, 90 10, 90 90)))', 'POLYGON ((10 10, 20 40, 90 90, 90 10, 10 10))']
|
1864
|
+
end
|
1865
|
+
|
1848
1866
|
def test_voronoi_diagram
|
1849
1867
|
skip unless ENV['FORCE_TESTS'] || Geos::Geometry.method_defined?(:voronoi_diagram)
|
1850
1868
|
|
@@ -70,6 +70,12 @@ class PreparedGeometryTests < Minitest::Test
|
|
70
70
|
relationship_tester(:contains?, true, false, false, false, false, true, false, false)
|
71
71
|
end
|
72
72
|
|
73
|
+
def test_contains_properly
|
74
|
+
skip unless ENV['FORCE_TESTS'] || defined?(Geos::PreparedGeometry)
|
75
|
+
|
76
|
+
relationship_tester(:contains_properly?, true, false, false, false, false, false, false, false)
|
77
|
+
end
|
78
|
+
|
73
79
|
def test_overlaps
|
74
80
|
skip unless ENV['FORCE_TESTS'] || defined?(Geos::PreparedGeometry)
|
75
81
|
|
@@ -111,4 +117,28 @@ class PreparedGeometryTests < Minitest::Test
|
|
111
117
|
Geos::PreparedGeometry.new('hello world')
|
112
118
|
end
|
113
119
|
end
|
120
|
+
|
121
|
+
def test_distance
|
122
|
+
skip unless ENV['FORCE_TESTS'] || (defined?(Geos::PreparedGeometry) && Geos::FFIGeos.respond_to?(:GEOSPreparedDistance_r))
|
123
|
+
|
124
|
+
assert_equal(5.0, read(POINT_A).to_prepared.distance(read(POINT_B)))
|
125
|
+
end
|
126
|
+
|
127
|
+
def test_distance_within
|
128
|
+
skip unless ENV['FORCE_TESTS'] || (defined?(Geos::PreparedGeometry) && Geos::FFIGeos.respond_to?(:GEOSPreparedDistanceWithin_r))
|
129
|
+
|
130
|
+
assert(read(POINT_A).to_prepared.distance_within?(read(POINT_B), 30.0))
|
131
|
+
refute(read(POINT_A).to_prepared.distance_within?(read(POINT_B), 3.0))
|
132
|
+
end
|
133
|
+
|
134
|
+
def test_nearest_points
|
135
|
+
skip unless ENV['FORCE_TESTS'] || (defined?(Geos::PreparedGeometry) && Geos::FFIGeos.respond_to?(:GEOSPreparedNearestPoints_r))
|
136
|
+
|
137
|
+
coord_seq = read('POLYGON((1 1, 1 5, 5 5, 5 1, 1 1))').to_prepared.nearest_points(read('POLYGON((8 8, 9 9, 9 10, 8 8))'))
|
138
|
+
|
139
|
+
assert_equal(5.0, coord_seq.x[0])
|
140
|
+
assert_equal(5.0, coord_seq.y[0])
|
141
|
+
assert_equal(8.0, coord_seq.x[1])
|
142
|
+
assert_equal(8.0, coord_seq.y[1])
|
143
|
+
end
|
114
144
|
end
|