ffi-geos 2.3.1 → 2.4.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|