-
Notifications
You must be signed in to change notification settings - Fork 224
/
spatial.rb
114 lines (99 loc) · 3.58 KB
/
spatial.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
# frozen_string_literal: true
module ActiveRecord
module ConnectionAdapters
module PostGIS
module OID
class Spatial < Type::Value
# sql_type is a string that comes from the database definition
# examples:
# "geometry(Point,4326)"
# "geography(Point,4326)"
# "geometry(Polygon,4326) NOT NULL"
# "geometry(Geography,4326)"
def initialize(oid, sql_type)
@sql_type = sql_type
@geo_type, @srid, @has_z, @has_m = self.class.parse_sql_type(sql_type)
end
# sql_type: geometry, geometry(Point), geometry(Point,4326), ...
#
# returns [geo_type, srid, has_z, has_m]
# geo_type: geography, geometry, point, line_string, polygon, ...
# srid: 1234
# has_z: false
# has_m: false
def self.parse_sql_type(sql_type)
geo_type, srid, has_z, has_m = nil, 0, false, false
if sql_type =~ /(geography|geometry)\((.*)\)$/i
# geometry(Point)
# geometry(Point,4326)
params = Regexp.last_match(2).split(",")
if params.first =~ /([a-z]+[^zm])(z?)(m?)/i
has_z = Regexp.last_match(2).length > 0
has_m = Regexp.last_match(3).length > 0
geo_type = Regexp.last_match(1)
end
if params.last =~ /(\d+)/
srid = Regexp.last_match(1).to_i
end
else
# geometry
# otherType(a,b)
geo_type = sql_type
end
[geo_type, srid, has_z, has_m]
end
def spatial_factory
@spatial_factory ||=
RGeo::ActiveRecord::SpatialFactoryStore.instance.factory(
geo_type: @geo_type,
has_m: @has_m,
has_z: @has_z,
sql_type: @sql_type,
srid: @srid
)
end
def geographic?
@sql_type =~ /geography/
end
def spatial?
true
end
def type
geographic? ? :geography : :geometry
end
# support setting an RGeo object or a WKT string
def serialize(value)
return if value.nil?
geo_value = cast_value(value)
# TODO - only valid types should be allowed
# e.g. linestring is not valid for point column
# raise "maybe should raise" unless RGeo::Feature::Geometry.check_type(geo_value)
RGeo::WKRep::WKBGenerator.new(hex_format: true, type_format: :ewkb, emit_ewkb_srid: true)
.generate(geo_value)
end
private
def cast_value(value)
return if value.nil?
String === value ? parse_wkt(value) : value
end
# convert WKT string into RGeo object
def parse_wkt(string)
wkt_parser(string).parse(string)
rescue RGeo::Error::ParseError
nil
end
def binary_string?(string)
string[0] == "\x00" || string[0] == "\x01" || string[0, 4] =~ /[0-9a-fA-F]{4}/
end
def wkt_parser(string)
if binary_string?(string)
RGeo::WKRep::WKBParser.new(spatial_factory, support_ewkb: true, default_srid: @srid)
else
RGeo::WKRep::WKTParser.new(spatial_factory, support_ewkt: true, default_srid: @srid)
end
end
end
end
end
end
end