ffi-geos 2.3.0 → 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 +240 -0
- data/.rubocop.yml +158 -54
- data/.rubocop_todo.yml +100 -0
- data/Gemfile +2 -0
- 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 +85 -1
- 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/test_helper.rb +6 -0
- data/test/wkb_writer_tests.rb +20 -0
- metadata +12 -4
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,
|
@@ -56,7 +60,13 @@ module Geos
|
|
56
60
|
elsif FFI::Platform::IS_WINDOWS
|
57
61
|
ENV['PATH'].split(File::PATH_SEPARATOR)
|
58
62
|
else
|
59
|
-
[
|
63
|
+
[
|
64
|
+
'/usr/local/{lib64,lib}',
|
65
|
+
'/opt/local/{lib64,lib}',
|
66
|
+
'/usr/{lib64,lib}',
|
67
|
+
'/opt/homebrew/lib',
|
68
|
+
'/usr/lib/{x86_64,i386,aarch64}-linux-gnu'
|
69
|
+
]
|
60
70
|
end
|
61
71
|
end
|
62
72
|
|
@@ -98,6 +108,11 @@ module Geos
|
|
98
108
|
:ndr, 1 # Little Endian
|
99
109
|
])
|
100
110
|
|
111
|
+
Geos::Flavors = enum(:flavor, [
|
112
|
+
:extended, 1,
|
113
|
+
:iso, 2
|
114
|
+
])
|
115
|
+
|
101
116
|
Geos::BufferCapStyles = enum(:buffer_cap_style, [
|
102
117
|
:round, 1,
|
103
118
|
:flat, 2,
|
@@ -581,6 +596,11 @@ module Geos
|
|
581
596
|
:pointer, :pointer, :pointer, :double, :int
|
582
597
|
],
|
583
598
|
|
599
|
+
GEOSConstrainedDelaunayTriangulation_r: [
|
600
|
+
# *geom, *handle, *geom
|
601
|
+
:pointer, :pointer, :pointer
|
602
|
+
],
|
603
|
+
|
584
604
|
GEOSVoronoiDiagram_r: [
|
585
605
|
# *geom, *handle, *geom, *envelope, tolerance, only_edges
|
586
606
|
:pointer, :pointer, :pointer, :pointer, :double, :int
|
@@ -721,6 +741,11 @@ module Geos
|
|
721
741
|
:int, :pointer, :pointer, :pointer, :pointer
|
722
742
|
],
|
723
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
|
+
|
724
749
|
GEOSHausdorffDistance_r: [
|
725
750
|
# (0 on exception, 1 otherwise), *handle, *geom_a, *geom_b, (double *) distance
|
726
751
|
:int, :pointer, :pointer, :pointer, :pointer
|
@@ -949,6 +974,21 @@ module Geos
|
|
949
974
|
# (2 on exception, 1 on true, 0 on false), *handle, *prepared, *geom
|
950
975
|
:char, :pointer, :pointer, :pointer
|
951
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
|
+
],
|
952
992
|
#### /PreparedGeometry functions ####
|
953
993
|
|
954
994
|
#### WktReader functions ####
|
@@ -1082,8 +1122,52 @@ module Geos
|
|
1082
1122
|
# void, *handle, *geom, bool
|
1083
1123
|
:void, :pointer, :pointer, :char
|
1084
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
|
+
],
|
1085
1135
|
#### /WkbWriter functions ####
|
1086
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
|
+
|
1087
1171
|
#### Linearref functions ####
|
1088
1172
|
GEOSProject_r: [
|
1089
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
|
data/test/test_helper.rb
CHANGED
@@ -9,6 +9,12 @@ SimpleCov.start do
|
|
9
9
|
add_filter '/.bundle/'
|
10
10
|
end
|
11
11
|
|
12
|
+
if ENV['CI']
|
13
|
+
require 'simplecov_json_formatter'
|
14
|
+
|
15
|
+
SimpleCov.formatter = SimpleCov::Formatter::JSONFormatter
|
16
|
+
end
|
17
|
+
|
12
18
|
require 'rubygems'
|
13
19
|
require 'minitest/autorun'
|
14
20
|
require 'minitest/reporters'
|