mapwkt 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/mapwkt/gmap/map.rb +140 -0
- data/lib/mapwkt/gmap/marker.rb +7 -0
- data/lib/mapwkt/gmap/overlay.rb +20 -0
- data/lib/mapwkt/gmap/polygon.rb +12 -0
- data/lib/mapwkt/gmap/polyline.rb +10 -0
- data/lib/mapwkt/wkt/geometry.rb +77 -0
- data/lib/mapwkt/wkt/line_string.rb +97 -0
- data/lib/mapwkt/wkt/point.rb +123 -0
- data/lib/mapwkt/wkt/polygon.rb +42 -0
- data/lib/mapwkt-safe.rb +17 -0
- data/lib/mapwkt.rb +44 -0
- metadata +56 -0
@@ -0,0 +1,140 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
class MapWKT::Map
|
4
|
+
def center
|
5
|
+
return @center if @center
|
6
|
+
return if self.overlays.empty?
|
7
|
+
|
8
|
+
n, s, e, w = nil
|
9
|
+
|
10
|
+
self.overlays.each do |o|
|
11
|
+
n = o.center.y if !n || o.center.y > n
|
12
|
+
s = o.center.y if !s || o.center.y < s
|
13
|
+
e = o.center.x if !e || o.center.x > e
|
14
|
+
w = o.center.x if !w || o.center.x < w
|
15
|
+
end
|
16
|
+
|
17
|
+
x = (e + w) / 2
|
18
|
+
y = (n + s) / 2
|
19
|
+
|
20
|
+
@center = MapWKT::Geometry::Point.new(y, x)
|
21
|
+
end
|
22
|
+
|
23
|
+
def center= (center)
|
24
|
+
return unless MapWKT::Geometry::Point === center
|
25
|
+
@center = center
|
26
|
+
end
|
27
|
+
|
28
|
+
def id
|
29
|
+
@id
|
30
|
+
end
|
31
|
+
|
32
|
+
def id= (id)
|
33
|
+
return unless id
|
34
|
+
raise(ArgumentError, "invalid HTML id: #{id.to_json}") unless id =~ /^[a-zA-Z][\w\d.:-]*$/
|
35
|
+
@id = id
|
36
|
+
end
|
37
|
+
|
38
|
+
def initialize (arg = {})
|
39
|
+
@overlays = []
|
40
|
+
|
41
|
+
self.options = case arg
|
42
|
+
when String then { id: arg }
|
43
|
+
when Hash then arg
|
44
|
+
else {}
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def js_initialize
|
49
|
+
"mapwkt_#{self.object_id}_initialize()"
|
50
|
+
end
|
51
|
+
|
52
|
+
def js_output (arg = {})
|
53
|
+
self.options = case arg
|
54
|
+
when String then { id: arg }
|
55
|
+
when Hash then arg
|
56
|
+
else {}
|
57
|
+
end
|
58
|
+
|
59
|
+
raise(ArgumentError, "HMTL id is required") unless self.id
|
60
|
+
|
61
|
+
<<-JAVASCRIPT.gsub('new google.maps.LatLng','new p')
|
62
|
+
function mapwkt_#{self.object_id}_initialize ()
|
63
|
+
{
|
64
|
+
var p = google.maps.LatLng
|
65
|
+
var element = document.getElementById('#{self.id}')
|
66
|
+
var options = { center: new google.maps.LatLng(#{self.center.latitude_f},#{self.center.longitude_f}), mapTypeId: google.maps.MapTypeId.#{self.map_type_id}, zoom: #{self.zoom} }
|
67
|
+
var map = new google.maps.Map(element, options)
|
68
|
+
#{self.overlays.map {|obj| obj.js_output('map') }.join("\n ")}
|
69
|
+
}
|
70
|
+
JAVASCRIPT
|
71
|
+
end
|
72
|
+
|
73
|
+
def map_type_id
|
74
|
+
@map_type_id ||= 'ROADMAP'
|
75
|
+
end
|
76
|
+
|
77
|
+
def map_type_id= (map_type_id)
|
78
|
+
return unless map_type_id
|
79
|
+
@map_type_id = ([map_type_id.to_s.upcase] & %w:HYBRID ROADMAP SATELLITE TERRAIN:).first || 'ROADMAP'
|
80
|
+
end
|
81
|
+
|
82
|
+
def options
|
83
|
+
{
|
84
|
+
center: self.center,
|
85
|
+
id: self.id,
|
86
|
+
map_type_id: self.map_type_id,
|
87
|
+
zoom: self.zoom
|
88
|
+
}
|
89
|
+
end
|
90
|
+
|
91
|
+
def options= (options)
|
92
|
+
options.symbolize_keys!
|
93
|
+
self.center = options[:center] || self.center
|
94
|
+
self.id = options[:id] || self.id
|
95
|
+
self.map_type_id = options[:map_type_id] || options[:mapTypeId] || self.map_type_id
|
96
|
+
self.zoom = options[:zoom] || self.zoom || 10
|
97
|
+
end
|
98
|
+
|
99
|
+
def overlays
|
100
|
+
self.refresh!
|
101
|
+
@overlays
|
102
|
+
end
|
103
|
+
|
104
|
+
def refresh!
|
105
|
+
return self if @refreshed
|
106
|
+
@refreshed = true
|
107
|
+
|
108
|
+
self.overlays.select! {|p| MapWKT::Overlay === p }
|
109
|
+
self
|
110
|
+
end
|
111
|
+
|
112
|
+
def zoom
|
113
|
+
@zoom ||= 10
|
114
|
+
end
|
115
|
+
|
116
|
+
def zoom= (zoom)
|
117
|
+
return unless zoom
|
118
|
+
@zoom = [0,[zoom.to_i, 25].min].max
|
119
|
+
end
|
120
|
+
|
121
|
+
def << (obj)
|
122
|
+
overlays = case obj
|
123
|
+
when MapWKT::Geometry then [MapWKT::Overlay.new(obj)]
|
124
|
+
when MapWKT::Overlay then [obj]
|
125
|
+
else
|
126
|
+
raise(TypeError, "method `wkt' is not defined for #{obj.inspect}") unless obj.respond_to?(:wkt)
|
127
|
+
|
128
|
+
MapWKT::Geometry.parse_wkt(obj.wkt).map do |g|
|
129
|
+
{
|
130
|
+
MapWKT::Geometry::Point => MapWKT::Overlay::Marker,
|
131
|
+
MapWKT::Geometry::LineString => MapWKT::Overlay::Polyline,
|
132
|
+
MapWKT::Geometry::Polygon => MapWKT::Overlay::Polygon
|
133
|
+
}[g.class].new(g, obj)
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
self.overlays.push(*overlays)
|
138
|
+
self
|
139
|
+
end
|
140
|
+
end
|
@@ -0,0 +1,7 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
class MapWKT::Overlay::Marker < MapWKT::Overlay
|
4
|
+
def js_output (map_name = nil)
|
5
|
+
"new google.maps.Marker({#{" map: #{map_name}," if map_name} position: new google.maps.LatLng(#{self.geometry.latitude_f},#{self.geometry.longitude_f}) })"
|
6
|
+
end
|
7
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
class MapWKT::Overlay
|
4
|
+
def center
|
5
|
+
self.geometry.center
|
6
|
+
end
|
7
|
+
|
8
|
+
def geometry
|
9
|
+
@geometry
|
10
|
+
end
|
11
|
+
|
12
|
+
def initialize (geometry, source)
|
13
|
+
@geometry = geometry
|
14
|
+
@source = source
|
15
|
+
end
|
16
|
+
|
17
|
+
def source
|
18
|
+
@source
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
class MapWKT::Overlay::Polygon < MapWKT::Overlay
|
4
|
+
def js_output (map_name = nil)
|
5
|
+
paths = [self.geometry.perimeter, *self.geometry.lacunae].map do |g|
|
6
|
+
points = [*g.points, g.points.first]
|
7
|
+
"[#{points.map {|p| "new google.maps.LatLng(#{p.latitude_f},#{p.longitude_f})" }.join(?,)}]"
|
8
|
+
end.join(?,)
|
9
|
+
|
10
|
+
"new google.maps.Polygon({#{" map: #{map_name}," if map_name} paths: [#{paths}] })"
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
class MapWKT::Overlay::Polyline < MapWKT::Overlay
|
4
|
+
def js_output (map_name = nil)
|
5
|
+
points = self.geometry.open? ? self.geometry.points : [*self.geometry.points, self.geometry.points.first]
|
6
|
+
path = "[#{points.map {|p| "new google.maps.LatLng(#{p.latitude_f},#{p.longitude_f})" }.join(?,)}]"
|
7
|
+
|
8
|
+
"new google.maps.Polyline({#{" map: #{map_name}," if map_name} path: #{path} })"
|
9
|
+
end
|
10
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
class MapWKT::Geometry
|
4
|
+
def self.parse_wkt (wkt_string)
|
5
|
+
# Remove extraneous whitespace.
|
6
|
+
s = wkt_string.to_s.upcase.strip.gsub(/\s*([),(])\s*/,'\1').gsub(/\s+/,' ')
|
7
|
+
|
8
|
+
case s
|
9
|
+
when /^POINT\(([\d\s.-]*?)\)$/
|
10
|
+
x, y = self.parse_x_y $1
|
11
|
+
[MapWKT::Geometry::Point.new(y,x)]
|
12
|
+
|
13
|
+
when /^LINESTRING\(([\d\s,.-]*?)\)$/
|
14
|
+
points = self.parse_points $1
|
15
|
+
[MapWKT::Geometry::LineString.new(*points)]
|
16
|
+
|
17
|
+
when /^POLYGON\((\([\d\s,.-]*?\)(?:,\([\d\s,.-]*?\))*)\)$/
|
18
|
+
perimeter, lacunae = self.parse_linestrings $1
|
19
|
+
[MapWKT::Geometry::Polygon.new(perimeter, *lacunae)]
|
20
|
+
|
21
|
+
when /^MULTIPOINT\(([\d\s,.-]*?)\)$/
|
22
|
+
self.parse_points $1
|
23
|
+
|
24
|
+
when /^MULTIPOINT\((\([\d\s,.-]*?\)(?:,\([\d\s,.-]*?\))*)\)$/
|
25
|
+
points_string = $1.gsub(/[()]/,'')
|
26
|
+
self.parse_points(points_string)
|
27
|
+
|
28
|
+
when /^MULTILINESTRING\((\([\d\s,.-]*?\)(?:,\([\d\s,.-]*?\))*)\)$/
|
29
|
+
self.parse_linestrings $1
|
30
|
+
|
31
|
+
when /^MULTIPOLYGON\((\(\([\d\s,.-]*?\)(?:,\([\d\s,.-]*?\))*\)(?:,\(\([\d\s,.-]*?\)(?:,\([\d\s,.-]*?\))*\))*)\)$/
|
32
|
+
self.parse_polygons $1
|
33
|
+
|
34
|
+
when /^GEOMETRYCOLLECTION\((.*?)\)$/
|
35
|
+
wkt_strings = $1.scan(/([A-Z]+\(.*?\))(?=,[A-Z]|$)/).flatten
|
36
|
+
wkt_strings.map {|wkt_string| self.parse_wkt(wkt_string) }.flatten
|
37
|
+
|
38
|
+
else
|
39
|
+
raise SyntaxError
|
40
|
+
end
|
41
|
+
|
42
|
+
rescue SyntaxError
|
43
|
+
raise SyntaxError, "not a valid WKT string: #{wkt_string.inspect}"
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.parse_polygons (polygons_string)
|
47
|
+
# Create Polygons from each "((x y, …), (x y, …))" in the polygons string.
|
48
|
+
polygons_string.scan(/\((\(.*?\))\)/).flatten.map do |linestrings_string|
|
49
|
+
perimeter, lacunae = self.parse_linestrings(linestrings_string)
|
50
|
+
MapWKT::Geometry::Polygon.new(perimeter, *lacunae)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def self.parse_linestrings (linestrings_string)
|
55
|
+
# Create LineStrings from each "(x y, …)" in the linestrings string.
|
56
|
+
linestrings_string.scan(/\((.*?)\)/).flatten.map do |points_string|
|
57
|
+
points = self.parse_points(points_string)
|
58
|
+
MapWKT::Geometry::LineString.new(*points)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def self.parse_points (points_string)
|
63
|
+
# Create Points from each "x y" in the points string.
|
64
|
+
points_string.split(?,).map do |x_y_string|
|
65
|
+
x, y = self.parse_x_y(x_y_string)
|
66
|
+
MapWKT::Geometry::Point.new(y,x)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def self.parse_x_y (x_y_string)
|
71
|
+
# Check the string.
|
72
|
+
raise SyntaxError unless x_y_string =~ /^(-?\d+(?:\.\d+)?)\s(-?\d+(?:\.\d+)?)$/
|
73
|
+
|
74
|
+
# Return the x and y values.
|
75
|
+
[$1, $2].map(&:to_f)
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
class MapWKT::Geometry::LineString < MapWKT::Geometry
|
4
|
+
# Returns a Point midway between the N/S- & E/W-most Points in the LineString.
|
5
|
+
def center
|
6
|
+
return if self.points.empty?
|
7
|
+
n, s, e, w = nil
|
8
|
+
|
9
|
+
self.points.each do |p|
|
10
|
+
n = p.y if !n || p.y > n
|
11
|
+
s = p.y if !s || p.y < s
|
12
|
+
e = p.x if !e || p.x > e
|
13
|
+
w = p.x if !w || p.x < w
|
14
|
+
end
|
15
|
+
|
16
|
+
x = (e + w) / 2
|
17
|
+
y = (n + s) / 2
|
18
|
+
|
19
|
+
Point.new(y, x)
|
20
|
+
end
|
21
|
+
|
22
|
+
# Returns this LineString after closing its endpoints.
|
23
|
+
def close!
|
24
|
+
self.refresh! unless @refreshed
|
25
|
+
@closed = true
|
26
|
+
self
|
27
|
+
end
|
28
|
+
|
29
|
+
# Returns true if this LineString is closed, false otherwise.
|
30
|
+
def closed?
|
31
|
+
self.refresh!
|
32
|
+
@closed ||= false
|
33
|
+
end
|
34
|
+
|
35
|
+
# Returns a new LineString with the same geographic Points as this LineString.
|
36
|
+
def dup
|
37
|
+
points = self.points.map(&:dup)
|
38
|
+
LineString(*points).tap {|ls| ls.close! if self.closed? }
|
39
|
+
end
|
40
|
+
|
41
|
+
# Returns a new LineString containing the given Points.
|
42
|
+
def initialize (*points)
|
43
|
+
@points = points
|
44
|
+
self.refresh!
|
45
|
+
end
|
46
|
+
|
47
|
+
# Returns false if this LineString is closed, true otherwise.
|
48
|
+
def open?
|
49
|
+
!self.closed?
|
50
|
+
end
|
51
|
+
|
52
|
+
# Returns this LineString after opening its endpoints.
|
53
|
+
def open!
|
54
|
+
self.refresh!
|
55
|
+
@closed = false
|
56
|
+
self
|
57
|
+
end
|
58
|
+
|
59
|
+
# Returns the array of Points in this LineString.
|
60
|
+
def points
|
61
|
+
self.refresh!
|
62
|
+
@points
|
63
|
+
end
|
64
|
+
|
65
|
+
# Removes any non-Point objects from this LineString's #points array. If the
|
66
|
+
# first Point has been added to the end of the #points array, closes the
|
67
|
+
# LineString and removes the Point. Returns this LineString.
|
68
|
+
def refresh!
|
69
|
+
return self if @refreshed
|
70
|
+
@refreshed = true
|
71
|
+
|
72
|
+
self.points.select! {|p| MapWKT::Geometry::Point === p }
|
73
|
+
|
74
|
+
while (self.points.many?) && (self.points.last == self.points.first)
|
75
|
+
self.points.pop
|
76
|
+
@closed = true
|
77
|
+
end
|
78
|
+
|
79
|
+
self
|
80
|
+
end
|
81
|
+
|
82
|
+
# Returns a string representation of this LineString. ⤿ indicates an open
|
83
|
+
# LineString, while ⟲ indicates a closed LineString.
|
84
|
+
def to_s
|
85
|
+
points = self.points.join(", ")
|
86
|
+
marker = self.closed? ? ?⟲ : ?⤿
|
87
|
+
|
88
|
+
"#{marker} #{points} #{marker} "
|
89
|
+
end
|
90
|
+
|
91
|
+
# Returns this LineString's WKT representation, with latitudes and longitudes
|
92
|
+
# rounded to 7 decimal places.
|
93
|
+
def wkt
|
94
|
+
points = self.open? ? self.points : [*self.points, self.points.first]
|
95
|
+
"LINESTRING(#{points.map {|p| "#{p.longitude_f} #{p.latitude_f}" }.join(", ")})"
|
96
|
+
end
|
97
|
+
end
|
@@ -0,0 +1,123 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
class MapWKT::Geometry::Point < MapWKT::Geometry
|
4
|
+
# Returns a new Point with the same latitude and longitude as this Point.
|
5
|
+
def center
|
6
|
+
self.dup
|
7
|
+
end
|
8
|
+
|
9
|
+
# Returns true if this Point lies in the eastern hemisphere, false otherwise.
|
10
|
+
def east?
|
11
|
+
self.longitude > 0
|
12
|
+
end
|
13
|
+
|
14
|
+
# Returns true if this Point lies on the equator, false otherwise.
|
15
|
+
def equator?
|
16
|
+
self.latitude == 0
|
17
|
+
end
|
18
|
+
|
19
|
+
# Returns a new MapWKT::Geometry::Point with latitude and longitude normalized
|
20
|
+
# to fit within the range [-90,90] for latitude and (-180,180] for longitude.
|
21
|
+
def initialize (φ, λ)
|
22
|
+
# Check arguments.
|
23
|
+
if [φ, λ].all? {|arg| arg.respond_to?(:to_f) }
|
24
|
+
φ = φ.to_f
|
25
|
+
λ = λ.to_f
|
26
|
+
else
|
27
|
+
raise ArgumentError, "expected (latitude, longitude), got (#{φ.inspect}, #{λ.inspect})"
|
28
|
+
end
|
29
|
+
|
30
|
+
# Normalize latitude (-90..90) and longitude (-179..180).
|
31
|
+
λ += 180 * (((φ / 90 + 1).floor / 2) % 2)
|
32
|
+
@y = 90 - (((90 + φ) % 360) - 180).abs
|
33
|
+
@x = self.pole? ? 0 : (λ - 180) % -360 + 180
|
34
|
+
end
|
35
|
+
|
36
|
+
# Returns this Point's latitude as a float, rounded to 7 decimal places.
|
37
|
+
def latitude
|
38
|
+
self.y.round(7)
|
39
|
+
end
|
40
|
+
|
41
|
+
# Returns this Point's latitude as a string, rounded to 7 decimal places.
|
42
|
+
def latitude_f
|
43
|
+
sprintf("%.7f", self.latitude).gsub(/\.?0*$/,'')
|
44
|
+
end
|
45
|
+
|
46
|
+
# Returns this Point's latitude in degrees N/S, rounded to 7 decimal places.
|
47
|
+
def latitude_°
|
48
|
+
φ = self.latitude
|
49
|
+
direction = (φ == 0) ? "" : (φ > 0) ? "N" : "S"
|
50
|
+
|
51
|
+
sprintf("%.7f°%s", φ.abs, direction).gsub(/\.?0*(?=°)/,'')
|
52
|
+
end
|
53
|
+
|
54
|
+
# Returns this Point's longitude as a float, rounded to 7 decimal places.
|
55
|
+
def longitude
|
56
|
+
self.x.round(7)
|
57
|
+
end
|
58
|
+
|
59
|
+
# Returns this Point's longitude as a string, rounded to 7 decimal places.
|
60
|
+
def longitude_f
|
61
|
+
sprintf("%.7f", self.longitude).gsub(/\.?0*$/,'')
|
62
|
+
end
|
63
|
+
|
64
|
+
# Returns this Point's longitude in degrees E/W, rounded to 7 decimal places.
|
65
|
+
def longitude_°
|
66
|
+
λ = self.longitude
|
67
|
+
direction = [0, 180].include?(λ) ? "" : (λ > 0) ? "E" : "W"
|
68
|
+
|
69
|
+
sprintf("%.7f°%s", λ.abs, direction).gsub(/\.?0*(?=°)/,'')
|
70
|
+
end
|
71
|
+
|
72
|
+
# Returns 1 if this Point is the north pole, -1 if it is the south pole, or nil
|
73
|
+
# if it is neither.
|
74
|
+
def pole?
|
75
|
+
return 1 if self.north_pole?
|
76
|
+
return -1 if self.south_pole?
|
77
|
+
end
|
78
|
+
|
79
|
+
# Returns true if this Point is the north pole.
|
80
|
+
def north_pole?
|
81
|
+
self.latitude == 90
|
82
|
+
end
|
83
|
+
|
84
|
+
# Returns true if this Point is the south pole.
|
85
|
+
def south_pole?
|
86
|
+
self.latitude == -90
|
87
|
+
end
|
88
|
+
|
89
|
+
# Returns a string representation of this Point.
|
90
|
+
def to_s
|
91
|
+
"#{self.latitude_°} #{self.longitude_°}"
|
92
|
+
end
|
93
|
+
|
94
|
+
# Returns true if this Point lies in the western hemisphere, false otherwise.
|
95
|
+
def west?
|
96
|
+
self.longitude < 0
|
97
|
+
end
|
98
|
+
|
99
|
+
# Returns this Point's WKT representation, rounded to 7 decimal places.
|
100
|
+
def wkt
|
101
|
+
"POINT(#{self.longitude_f} #{self.latitude_f})"
|
102
|
+
end
|
103
|
+
|
104
|
+
# Returns this Point's longitude as a float.
|
105
|
+
def x
|
106
|
+
@x
|
107
|
+
end
|
108
|
+
|
109
|
+
# Returns this Point's latitude as a float.
|
110
|
+
def y
|
111
|
+
@y
|
112
|
+
end
|
113
|
+
|
114
|
+
# Returns true if this Point matches the other Point, within 7 decimal places.
|
115
|
+
def == (other)
|
116
|
+
[self.latitude, self.longitude] == [other.latitude, other.longitude]
|
117
|
+
end
|
118
|
+
|
119
|
+
# Returns true if this Point matches the other Point exactly.
|
120
|
+
def === (other)
|
121
|
+
[self.x, self.y] == [other.x, other.y]
|
122
|
+
end
|
123
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
class MapWKT::Geometry::Polygon < MapWKT::Geometry
|
4
|
+
# Returns a Point midway between the N/S- & E/W-most Points in the perimeter.
|
5
|
+
def center
|
6
|
+
self.perimeter.center
|
7
|
+
end
|
8
|
+
|
9
|
+
# Returns a new MapWKT::Geometry::Polygon with the given outer perimeter and
|
10
|
+
# internal lacunae. If the given LineStrings were unclosed, closes them for
|
11
|
+
# use in the Polygon, but leaves the original LineStrings unchanged.
|
12
|
+
def initialize (perimeter, *lacunae)
|
13
|
+
@perimeter = perimeter.dup.close!
|
14
|
+
@lacunae = lacunae.map {|lacuna| lacuna.dup.close! }
|
15
|
+
end
|
16
|
+
|
17
|
+
# Returns an array of LineStrings representing the geographical areas treated
|
18
|
+
# as lacunae in this Polygon's enclosed area. Does not return the objects
|
19
|
+
# themselves, nor the actual array containing them.
|
20
|
+
def lacunae
|
21
|
+
@lacunae.map(&:dup)
|
22
|
+
end
|
23
|
+
|
24
|
+
# Returns a LineString representing the geographical area treated as the
|
25
|
+
# outer perimeter of this Polygon's enclosed area. Does not return the actual
|
26
|
+
# LineString object used in this Polygon.
|
27
|
+
def perimeter
|
28
|
+
@perimeter.dup
|
29
|
+
end
|
30
|
+
|
31
|
+
# Returns a string representation of this Polygon. ⭓ indicates the perimeter
|
32
|
+
# LineString, while ⬠ indicates a lacuna LineString.
|
33
|
+
def to_s
|
34
|
+
"⭓ #{@perimeter.points.join(", ")} ⭓#{@lacunae.map {|lacuna| " | ⬠ #{lacuna.points.join(", ")} ⬠" }.join} "
|
35
|
+
end
|
36
|
+
|
37
|
+
# Returns this Polygon's WKT representation, with latitudes and longitudes
|
38
|
+
# rounded to 7 decimal places.
|
39
|
+
def wkt
|
40
|
+
"POLYGON((#{[*@perimeter.points, @perimeter.points.first].map {|p| "#{p.longitude_f} #{p.latitude_f}" }.join(", ")})#{@lacunae.map {|lacuna| ", (#{[*lacuna.points, lacuna.points.first].map {|p| "#{p.longitude_f} #{p.latitude_f}" }.join(", ")})" }.join })"
|
41
|
+
end
|
42
|
+
end
|
data/lib/mapwkt-safe.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module MapWKT
|
4
|
+
require 'bundler/setup'
|
5
|
+
require 'active_support/all'
|
6
|
+
|
7
|
+
require_relative 'mapwkt/gmap/overlay'
|
8
|
+
require_relative 'mapwkt/gmap/map'
|
9
|
+
require_relative 'mapwkt/gmap/marker'
|
10
|
+
require_relative 'mapwkt/gmap/polygon'
|
11
|
+
require_relative 'mapwkt/gmap/polyline'
|
12
|
+
|
13
|
+
require_relative 'mapwkt/wkt/geometry'
|
14
|
+
require_relative 'mapwkt/wkt/line_string'
|
15
|
+
require_relative 'mapwkt/wkt/point'
|
16
|
+
require_relative 'mapwkt/wkt/polygon'
|
17
|
+
end
|
data/lib/mapwkt.rb
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
Geometry = Class.new
|
4
|
+
Map = Class.new
|
5
|
+
Overlay = Class.new
|
6
|
+
|
7
|
+
LineString = Class.new(Geometry)
|
8
|
+
Point = Class.new(Geometry)
|
9
|
+
Polygon = Class.new(Geometry)
|
10
|
+
|
11
|
+
Marker = Class.new(Overlay)
|
12
|
+
PolygonOverlay = Class.new(Overlay)
|
13
|
+
Polyline = Class.new(Overlay)
|
14
|
+
|
15
|
+
# Register the class names in the top-level namespace.
|
16
|
+
[
|
17
|
+
Geometry, LineString, Map, Marker, Overlay,
|
18
|
+
Point, Polygon, PolygonOverlay, Polyline
|
19
|
+
].map(&:name)
|
20
|
+
|
21
|
+
# Create the namespaced constants pointing to the classes.
|
22
|
+
module MapWKT
|
23
|
+
Geometry = ::Geometry
|
24
|
+
Map = ::Map
|
25
|
+
Overlay = ::Overlay
|
26
|
+
|
27
|
+
class Geometry
|
28
|
+
LineString = ::LineString
|
29
|
+
Point = ::Point
|
30
|
+
Polygon = ::Polygon
|
31
|
+
end
|
32
|
+
|
33
|
+
class Overlay
|
34
|
+
Marker = ::Marker
|
35
|
+
Polygon = ::PolygonOverlay
|
36
|
+
Polyline = ::Polyline
|
37
|
+
end
|
38
|
+
|
39
|
+
require_relative 'mapwkt-safe'
|
40
|
+
end
|
41
|
+
|
42
|
+
def Point (*args) Point.new(*args) end
|
43
|
+
def LineString (*args) LineString.new(*args) end
|
44
|
+
def Polygon (*args) Polygon.new(*args) end
|
metadata
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: mapwkt
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Jesse Sielaff
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-02-11 00:00:00.000000000 Z
|
13
|
+
dependencies: []
|
14
|
+
description: .
|
15
|
+
email: jesse.sielaff@gmail.com
|
16
|
+
executables: []
|
17
|
+
extensions: []
|
18
|
+
extra_rdoc_files: []
|
19
|
+
files:
|
20
|
+
- ./lib/mapwkt/gmap/map.rb
|
21
|
+
- ./lib/mapwkt/gmap/marker.rb
|
22
|
+
- ./lib/mapwkt/gmap/overlay.rb
|
23
|
+
- ./lib/mapwkt/gmap/polygon.rb
|
24
|
+
- ./lib/mapwkt/gmap/polyline.rb
|
25
|
+
- ./lib/mapwkt/wkt/geometry.rb
|
26
|
+
- ./lib/mapwkt/wkt/line_string.rb
|
27
|
+
- ./lib/mapwkt/wkt/point.rb
|
28
|
+
- ./lib/mapwkt/wkt/polygon.rb
|
29
|
+
- ./lib/mapwkt-safe.rb
|
30
|
+
- ./lib/mapwkt.rb
|
31
|
+
homepage:
|
32
|
+
licenses: []
|
33
|
+
post_install_message:
|
34
|
+
rdoc_options: []
|
35
|
+
require_paths:
|
36
|
+
- lib
|
37
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
38
|
+
none: false
|
39
|
+
requirements:
|
40
|
+
- - ! '>='
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
version: '0'
|
43
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
44
|
+
none: false
|
45
|
+
requirements:
|
46
|
+
- - ! '>='
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '0'
|
49
|
+
requirements: []
|
50
|
+
rubyforge_project:
|
51
|
+
rubygems_version: 1.8.23
|
52
|
+
signing_key:
|
53
|
+
specification_version: 3
|
54
|
+
summary: An object library for converting Ruby objects with WKT to JavaScript for
|
55
|
+
Google Maps.
|
56
|
+
test_files: []
|