libgd-gis 0.2.6 → 0.2.7.pre.alpha.1
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/lib/gd/gis/crs_normalizer.rb +57 -0
- data/lib/gd/gis/feature.rb +3 -2
- data/lib/gd/gis/input/detector.rb +34 -0
- data/lib/gd/gis/input/geojson.rb +0 -0
- data/lib/gd/gis/input/kml.rb +0 -0
- data/lib/gd/gis/input/shapefile.rb +0 -0
- data/lib/gd/gis/input.rb +0 -0
- data/lib/gd/gis/layer_geojson.rb +53 -3
- data/lib/gd/gis/layer_points.rb +3 -0
- data/lib/gd/gis/map.rb +76 -17
- data/lib/gd/gis/ontology.rb +26 -0
- data/lib/gd/gis/ontology.yml +267 -0
- data/lib/gd/gis/path_sampler.rb +68 -0
- data/lib/gd/gis.rb +1 -0
- metadata +15 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6559ec92e07ae18e0a783d5d822c676437c68896928d7e2a59143ffd0ef9bb7e
|
|
4
|
+
data.tar.gz: d1d343b32dbef5a5093746577db4b61d678a27d42949737079f3ba8e4c840a9a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 40cdba7a5707db6afde50b1358e6ae65eb936e34880d4ba2d593acfa72945cb09dc25661bab3dfdf7eefc6ea7f260171f9c38bc396dbeb2c42c83fc573964bd7
|
|
7
|
+
data.tar.gz: ac76fd31a7b28acae6067ed56ed4783d2dabb97992d19c841af25459204caf9e3180cd5bea9e74b9de8c583741ab187044ccbdcabd45ce1b2523438faf5c1b5b
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
module GD
|
|
2
|
+
module GIS
|
|
3
|
+
module CRS
|
|
4
|
+
CRS84 = "urn:ogc:def:crs:OGC:1.3:CRS84"
|
|
5
|
+
EPSG4326 = "EPSG:4326"
|
|
6
|
+
EPSG3857 = "EPSG:3857"
|
|
7
|
+
|
|
8
|
+
class Normalizer
|
|
9
|
+
def initialize(crs)
|
|
10
|
+
@crs = normalize_name(crs)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# Accepts:
|
|
14
|
+
# normalize(lon,lat)
|
|
15
|
+
# normalize(lon,lat,z)
|
|
16
|
+
# normalize([lon,lat])
|
|
17
|
+
# normalize([lon,lat,z])
|
|
18
|
+
def normalize(*args)
|
|
19
|
+
lon, lat = args.flatten
|
|
20
|
+
return nil if lon.nil? || lat.nil?
|
|
21
|
+
|
|
22
|
+
lon = lon.to_f
|
|
23
|
+
lat = lat.to_f
|
|
24
|
+
|
|
25
|
+
case @crs
|
|
26
|
+
when CRS84, nil
|
|
27
|
+
[lon, lat]
|
|
28
|
+
|
|
29
|
+
when EPSG4326
|
|
30
|
+
# axis order lat,lon → lon,lat
|
|
31
|
+
[lat, lon]
|
|
32
|
+
|
|
33
|
+
when EPSG3857
|
|
34
|
+
mercator_to_wgs84(lon, lat)
|
|
35
|
+
|
|
36
|
+
else
|
|
37
|
+
[lon, lat]
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
private
|
|
42
|
+
|
|
43
|
+
def normalize_name(name)
|
|
44
|
+
return nil if name.nil?
|
|
45
|
+
name.to_s.strip
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def mercator_to_wgs84(x, y)
|
|
49
|
+
r = 6378137.0
|
|
50
|
+
lon = (x / r) * 180.0 / Math::PI
|
|
51
|
+
lat = (2 * Math.atan(Math.exp(y / r)) - Math::PI / 2) * 180.0 / Math::PI
|
|
52
|
+
[lon, lat]
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
data/lib/gd/gis/feature.rb
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
module GD
|
|
2
2
|
module GIS
|
|
3
3
|
class Feature
|
|
4
|
-
attr_reader :geometry, :properties
|
|
4
|
+
attr_reader :geometry, :properties, :layer
|
|
5
5
|
|
|
6
|
-
def initialize(geometry, properties)
|
|
6
|
+
def initialize(geometry, properties, layer = nil)
|
|
7
7
|
@geometry = geometry
|
|
8
8
|
@properties = properties || {}
|
|
9
|
+
@layer = layer
|
|
9
10
|
end
|
|
10
11
|
|
|
11
12
|
# -------------------------------------------------
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
module GD
|
|
2
|
+
module GIS
|
|
3
|
+
module Input
|
|
4
|
+
module Detector
|
|
5
|
+
def self.detect(path)
|
|
6
|
+
return :geojson if geojson?(path)
|
|
7
|
+
return :kml if kml?(path)
|
|
8
|
+
return :shapefile if shapefile?(path)
|
|
9
|
+
return :osm_pbf if pbf?(path)
|
|
10
|
+
:unknown
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def self.geojson?(path)
|
|
14
|
+
File.open(path) do |f|
|
|
15
|
+
head = f.read(2048)
|
|
16
|
+
head.include?('"FeatureCollection"') || head.include?('"GeometryCollection"')
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def self.kml?(path)
|
|
21
|
+
File.open(path) { |f| f.read(512).include?("<kml") }
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def self.shapefile?(path)
|
|
25
|
+
File.open(path, "rb") { |f| f.read(4) == "\x00\x00\x27\x0A" }
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def self.pbf?(path)
|
|
29
|
+
File.open(path, "rb") { |f| f.read(2) == "\x1f\x8b" }
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
data/lib/gd/gis/input.rb
ADDED
|
File without changes
|
data/lib/gd/gis/layer_geojson.rb
CHANGED
|
@@ -1,16 +1,66 @@
|
|
|
1
1
|
require "json"
|
|
2
2
|
require_relative "feature"
|
|
3
|
+
require_relative "crs_normalizer"
|
|
4
|
+
require_relative "ontology"
|
|
3
5
|
|
|
4
6
|
module GD
|
|
5
7
|
module GIS
|
|
6
8
|
class LayerGeoJSON
|
|
9
|
+
|
|
7
10
|
def self.load(path)
|
|
8
11
|
data = JSON.parse(File.read(path))
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
+
|
|
13
|
+
# 1) Detect CRS
|
|
14
|
+
crs_name = data["crs"]&.dig("properties", "name")
|
|
15
|
+
normalizer = CRS::Normalizer.new(crs_name)
|
|
16
|
+
|
|
17
|
+
# 2) Load ontology
|
|
18
|
+
ontology = Ontology.new
|
|
19
|
+
|
|
20
|
+
# 3) Normalize geometries + classify
|
|
21
|
+
data["features"].map do |f|
|
|
22
|
+
normalize_geometry!(f["geometry"], normalizer)
|
|
23
|
+
layer = ontology.classify(f["properties"] || {})
|
|
24
|
+
Feature.new(f["geometry"], f["properties"], layer)
|
|
12
25
|
end
|
|
13
26
|
end
|
|
27
|
+
|
|
28
|
+
# --------------------------------------------
|
|
29
|
+
# CRS normalization (2D + 3D safe)
|
|
30
|
+
# --------------------------------------------
|
|
31
|
+
def self.normalize_geometry!(geometry, normalizer)
|
|
32
|
+
case geometry["type"]
|
|
33
|
+
|
|
34
|
+
when "Point"
|
|
35
|
+
geometry["coordinates"] =
|
|
36
|
+
normalizer.normalize(geometry["coordinates"])
|
|
37
|
+
|
|
38
|
+
when "LineString"
|
|
39
|
+
geometry["coordinates"] =
|
|
40
|
+
geometry["coordinates"].map { |c| normalizer.normalize(c) }
|
|
41
|
+
|
|
42
|
+
when "MultiLineString"
|
|
43
|
+
geometry["coordinates"] =
|
|
44
|
+
geometry["coordinates"].map do |line|
|
|
45
|
+
line.map { |c| normalizer.normalize(c) }
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
when "Polygon"
|
|
49
|
+
geometry["coordinates"] =
|
|
50
|
+
geometry["coordinates"].map do |ring|
|
|
51
|
+
ring.map { |c| normalizer.normalize(c) }
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
when "MultiPolygon"
|
|
55
|
+
geometry["coordinates"] =
|
|
56
|
+
geometry["coordinates"].map do |poly|
|
|
57
|
+
poly.map do |ring|
|
|
58
|
+
ring.map { |c| normalizer.normalize(c) }
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
14
64
|
end
|
|
15
65
|
end
|
|
16
66
|
end
|
data/lib/gd/gis/layer_points.rb
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
module GD
|
|
2
2
|
module GIS
|
|
3
3
|
class PointsLayer
|
|
4
|
+
attr_accessor :data, :size
|
|
5
|
+
|
|
4
6
|
def initialize(data, lon:, lat:, icon:, label: nil, font: nil, size: 12, color: [0,0,0])
|
|
5
7
|
@data = data
|
|
6
8
|
@lon = lon
|
|
@@ -15,6 +17,7 @@ module GD
|
|
|
15
17
|
@label = label
|
|
16
18
|
@font = font
|
|
17
19
|
@size = size
|
|
20
|
+
@data = data
|
|
18
21
|
@color = color
|
|
19
22
|
|
|
20
23
|
@icon.alpha_blending = true
|
data/lib/gd/gis/map.rb
CHANGED
|
@@ -36,6 +36,10 @@ module GD
|
|
|
36
36
|
@lines_layers = []
|
|
37
37
|
@polygons_layers = []
|
|
38
38
|
|
|
39
|
+
@dynamic_points = []
|
|
40
|
+
@dynamic_lines = []
|
|
41
|
+
@dynamic_polys = []
|
|
42
|
+
|
|
39
43
|
@style = nil
|
|
40
44
|
end
|
|
41
45
|
|
|
@@ -47,18 +51,27 @@ module GD
|
|
|
47
51
|
features = LayerGeoJSON.load(path)
|
|
48
52
|
|
|
49
53
|
features.each do |feature|
|
|
50
|
-
|
|
51
|
-
|
|
54
|
+
case feature.layer
|
|
55
|
+
when :water
|
|
56
|
+
# optional: detect river vs canal from properties
|
|
57
|
+
kind =
|
|
58
|
+
case (feature.properties["objeto"] || feature.properties["waterway"]).to_s.downcase
|
|
59
|
+
when /river|río/ then :river
|
|
60
|
+
when /stream|arroyo/ then :stream
|
|
61
|
+
else :minor
|
|
62
|
+
end
|
|
63
|
+
|
|
52
64
|
@layers[:water] << [kind, feature]
|
|
53
65
|
|
|
54
|
-
|
|
55
|
-
|
|
66
|
+
when :roads
|
|
67
|
+
# map to style categories if you want later
|
|
68
|
+
@layers[:street] << feature
|
|
56
69
|
|
|
57
|
-
|
|
58
|
-
@layers[:
|
|
70
|
+
when :parks
|
|
71
|
+
@layers[:park] << feature
|
|
59
72
|
|
|
60
|
-
|
|
61
|
-
|
|
73
|
+
else
|
|
74
|
+
# ignore unclassified for now
|
|
62
75
|
end
|
|
63
76
|
end
|
|
64
77
|
end
|
|
@@ -68,7 +81,9 @@ module GD
|
|
|
68
81
|
# -----------------------------------
|
|
69
82
|
|
|
70
83
|
def add_points(data, **opts)
|
|
71
|
-
|
|
84
|
+
layer = GD::GIS::PointsLayer.new(data, **opts)
|
|
85
|
+
@points_layers << layer
|
|
86
|
+
layer
|
|
72
87
|
end
|
|
73
88
|
|
|
74
89
|
def add_line(coords, **opts)
|
|
@@ -136,20 +151,26 @@ module GD
|
|
|
136
151
|
)
|
|
137
152
|
end
|
|
138
153
|
|
|
139
|
-
projection = lambda do |lon, lat|
|
|
154
|
+
@projection = lambda do |lon, lat|
|
|
140
155
|
x, y = GD::GIS::Projection.lonlat_to_global_px(lon, lat, @zoom)
|
|
141
156
|
[(x - origin_x).round, (y - origin_y).round]
|
|
142
157
|
end
|
|
143
158
|
|
|
144
159
|
# 1️⃣ Semantic GeoJSON layers (this is what was working)
|
|
145
160
|
@style.order.each do |kind|
|
|
146
|
-
draw_layer(kind, projection)
|
|
161
|
+
draw_layer(kind, @projection)
|
|
147
162
|
end
|
|
148
163
|
|
|
149
164
|
# 2️⃣ Generic overlays
|
|
150
|
-
@polygons_layers.each { |l| l.render!(@image, projection) }
|
|
151
|
-
@lines_layers.each { |l| l.render!(@image, projection) }
|
|
152
|
-
@points_layers.each { |l| l.render!(@image, projection) }
|
|
165
|
+
@polygons_layers.each { |l| l.render!(@image, @projection) }
|
|
166
|
+
@lines_layers.each { |l| l.render!(@image, @projection) }
|
|
167
|
+
@points_layers.each { |l| l.render!(@image, @projection) }
|
|
168
|
+
|
|
169
|
+
@dynamic_polys.each { |l| l.render!(@image, @projection) }
|
|
170
|
+
@dynamic_lines.each { |l| l.render!(@image, @projection) }
|
|
171
|
+
@dynamic_points.each { |l| l.render!(@image, @projection) }
|
|
172
|
+
|
|
173
|
+
@image
|
|
153
174
|
end
|
|
154
175
|
|
|
155
176
|
def save(path)
|
|
@@ -186,7 +207,7 @@ module GD
|
|
|
186
207
|
|
|
187
208
|
if style[:stroke]
|
|
188
209
|
color = GD::Color.rgb(*style[:stroke])
|
|
189
|
-
f.draw(@image, projection, color, width, :water)
|
|
210
|
+
f.draw(@image, @projection, color, width, :water)
|
|
190
211
|
end
|
|
191
212
|
|
|
192
213
|
else
|
|
@@ -195,18 +216,56 @@ module GD
|
|
|
195
216
|
|
|
196
217
|
if geom == "Polygon" || geom == "MultiPolygon"
|
|
197
218
|
# THIS is the critical fix
|
|
198
|
-
f.draw(@image, projection, nil, nil, style)
|
|
219
|
+
f.draw(@image, @projection, nil, nil, style)
|
|
199
220
|
else
|
|
200
221
|
if style[:stroke]
|
|
201
222
|
color = GD::Color.rgb(*style[:stroke])
|
|
202
223
|
width = style[:stroke_width] || 1
|
|
203
|
-
f.draw(@image, projection, color, width)
|
|
224
|
+
f.draw(@image, @projection, color, width)
|
|
204
225
|
end
|
|
205
226
|
end
|
|
206
227
|
end
|
|
207
228
|
end
|
|
208
229
|
end
|
|
209
230
|
|
|
231
|
+
def clear_dynamic_layers
|
|
232
|
+
@dynamic_points.clear
|
|
233
|
+
@dynamic_lines.clear
|
|
234
|
+
@dynamic_polys.clear
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
def add_dynamic_point(data, **opts)
|
|
238
|
+
@dynamic_points << GD::GIS::PointsLayer.new(data, **opts)
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
def add_dynamic_line(coords, **opts)
|
|
242
|
+
feature = {
|
|
243
|
+
"type" => "Feature",
|
|
244
|
+
"geometry" => {
|
|
245
|
+
"type" => "LineString",
|
|
246
|
+
"coordinates" => coords
|
|
247
|
+
},
|
|
248
|
+
"properties" => {}
|
|
249
|
+
}
|
|
250
|
+
@dynamic_lines << GD::GIS::LinesLayer.new([feature], **opts)
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
def render_base
|
|
254
|
+
render
|
|
255
|
+
@base_image = @image
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
def render_with_base
|
|
259
|
+
img = GD::Image.new(@base_image.width, @base_image.height)
|
|
260
|
+
img.copy(@base_image, 0,0, 0,0, @base_image.width, @base_image.height)
|
|
261
|
+
|
|
262
|
+
@points_layers.each { |l| l.render!(img, @projection) }
|
|
263
|
+
@lines_layers.each { |l| l.render!(img, @projection) }
|
|
264
|
+
@polygons_layers.each{ |l| l.render!(img, @projection) }
|
|
265
|
+
|
|
266
|
+
img
|
|
267
|
+
end
|
|
268
|
+
|
|
210
269
|
private
|
|
211
270
|
|
|
212
271
|
def add_lines(features, **opts)
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
require "yaml"
|
|
2
|
+
|
|
3
|
+
module GD
|
|
4
|
+
module GIS
|
|
5
|
+
class Ontology
|
|
6
|
+
def initialize(path = nil)
|
|
7
|
+
path ||= File.expand_path("ontology.yml", __dir__)
|
|
8
|
+
@rules = YAML.load_file(path)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def classify(properties)
|
|
12
|
+
@rules.each do |layer, sources|
|
|
13
|
+
sources.each do |source, rules|
|
|
14
|
+
rules.each do |key, values|
|
|
15
|
+
v = (properties[key.to_s] || properties[key.to_sym]).to_s.strip.downcase
|
|
16
|
+
values = values.map { |x| x.to_s.downcase }
|
|
17
|
+
|
|
18
|
+
return layer.to_sym if values.any? { |x| v.include?(x) }
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
nil
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
# ============================================================
|
|
2
|
+
# WATER
|
|
3
|
+
# ============================================================
|
|
4
|
+
|
|
5
|
+
water:
|
|
6
|
+
ign:
|
|
7
|
+
objeto:
|
|
8
|
+
- canal
|
|
9
|
+
- río
|
|
10
|
+
- arroyo
|
|
11
|
+
- embalse
|
|
12
|
+
- laguna
|
|
13
|
+
- dique
|
|
14
|
+
- represa
|
|
15
|
+
gna:
|
|
16
|
+
- canal
|
|
17
|
+
- río
|
|
18
|
+
- arroyo
|
|
19
|
+
- embalse
|
|
20
|
+
- laguna
|
|
21
|
+
|
|
22
|
+
natural_earth:
|
|
23
|
+
featurecla:
|
|
24
|
+
- river
|
|
25
|
+
- lake
|
|
26
|
+
- reservoir
|
|
27
|
+
- riverbank
|
|
28
|
+
|
|
29
|
+
osm:
|
|
30
|
+
waterway:
|
|
31
|
+
- river
|
|
32
|
+
- stream
|
|
33
|
+
- canal
|
|
34
|
+
- drain
|
|
35
|
+
- ditch
|
|
36
|
+
natural:
|
|
37
|
+
- water
|
|
38
|
+
- lake
|
|
39
|
+
- reservoir
|
|
40
|
+
- wetland
|
|
41
|
+
- bay
|
|
42
|
+
- strait
|
|
43
|
+
|
|
44
|
+
# ============================================================
|
|
45
|
+
# ROADS
|
|
46
|
+
# ============================================================
|
|
47
|
+
|
|
48
|
+
roads:
|
|
49
|
+
ign:
|
|
50
|
+
objeto:
|
|
51
|
+
- autopista
|
|
52
|
+
- ruta
|
|
53
|
+
- camino
|
|
54
|
+
- calle
|
|
55
|
+
- avenida
|
|
56
|
+
|
|
57
|
+
natural_earth:
|
|
58
|
+
featurecla:
|
|
59
|
+
- road
|
|
60
|
+
- highway
|
|
61
|
+
|
|
62
|
+
osm:
|
|
63
|
+
highway:
|
|
64
|
+
- motorway
|
|
65
|
+
- motorway_link
|
|
66
|
+
- trunk
|
|
67
|
+
- trunk_link
|
|
68
|
+
- primary
|
|
69
|
+
- primary_link
|
|
70
|
+
- secondary
|
|
71
|
+
- secondary_link
|
|
72
|
+
- tertiary
|
|
73
|
+
- tertiary_link
|
|
74
|
+
- residential
|
|
75
|
+
- unclassified
|
|
76
|
+
- service
|
|
77
|
+
- living_street
|
|
78
|
+
- pedestrian
|
|
79
|
+
- road
|
|
80
|
+
- busway
|
|
81
|
+
- track
|
|
82
|
+
|
|
83
|
+
# ============================================================
|
|
84
|
+
# RAIL
|
|
85
|
+
# ============================================================
|
|
86
|
+
|
|
87
|
+
rail:
|
|
88
|
+
osm:
|
|
89
|
+
railway:
|
|
90
|
+
- rail
|
|
91
|
+
- subway
|
|
92
|
+
- light_rail
|
|
93
|
+
- tram
|
|
94
|
+
- monorail
|
|
95
|
+
- funicular
|
|
96
|
+
|
|
97
|
+
# ============================================================
|
|
98
|
+
# PARKS & GREEN
|
|
99
|
+
# ============================================================
|
|
100
|
+
|
|
101
|
+
park:
|
|
102
|
+
ign:
|
|
103
|
+
objeto:
|
|
104
|
+
- parque
|
|
105
|
+
- plaza
|
|
106
|
+
- reserva
|
|
107
|
+
|
|
108
|
+
natural_earth:
|
|
109
|
+
featurecla:
|
|
110
|
+
- park
|
|
111
|
+
- protected_area
|
|
112
|
+
|
|
113
|
+
osm:
|
|
114
|
+
leisure:
|
|
115
|
+
- park
|
|
116
|
+
- garden
|
|
117
|
+
- pitch
|
|
118
|
+
- playground
|
|
119
|
+
- recreation_ground
|
|
120
|
+
- golf_course
|
|
121
|
+
landuse:
|
|
122
|
+
- grass
|
|
123
|
+
- meadow
|
|
124
|
+
- forest
|
|
125
|
+
natural:
|
|
126
|
+
- wood
|
|
127
|
+
- scrub
|
|
128
|
+
- heath
|
|
129
|
+
- grassland
|
|
130
|
+
|
|
131
|
+
# ============================================================
|
|
132
|
+
# BUILDINGS
|
|
133
|
+
# ============================================================
|
|
134
|
+
|
|
135
|
+
buildings:
|
|
136
|
+
ign:
|
|
137
|
+
objeto:
|
|
138
|
+
- edificio
|
|
139
|
+
- hospital
|
|
140
|
+
- escuela
|
|
141
|
+
|
|
142
|
+
osm:
|
|
143
|
+
building:
|
|
144
|
+
- yes
|
|
145
|
+
- residential
|
|
146
|
+
- commercial
|
|
147
|
+
- industrial
|
|
148
|
+
- retail
|
|
149
|
+
- hospital
|
|
150
|
+
- school
|
|
151
|
+
- church
|
|
152
|
+
- cathedral
|
|
153
|
+
- university
|
|
154
|
+
- public
|
|
155
|
+
- civic
|
|
156
|
+
- apartments
|
|
157
|
+
|
|
158
|
+
# ============================================================
|
|
159
|
+
# POI (Points of Interest)
|
|
160
|
+
# ============================================================
|
|
161
|
+
|
|
162
|
+
poi:
|
|
163
|
+
osm:
|
|
164
|
+
amenity:
|
|
165
|
+
- hospital
|
|
166
|
+
- school
|
|
167
|
+
- university
|
|
168
|
+
- cafe
|
|
169
|
+
- restaurant
|
|
170
|
+
- bar
|
|
171
|
+
- pharmacy
|
|
172
|
+
- bank
|
|
173
|
+
- atm
|
|
174
|
+
- police
|
|
175
|
+
- fire_station
|
|
176
|
+
- library
|
|
177
|
+
- cinema
|
|
178
|
+
- theatre
|
|
179
|
+
- post_office
|
|
180
|
+
- fuel
|
|
181
|
+
shop:
|
|
182
|
+
- supermarket
|
|
183
|
+
- bakery
|
|
184
|
+
- convenience
|
|
185
|
+
- clothes
|
|
186
|
+
- mall
|
|
187
|
+
- department_store
|
|
188
|
+
- butcher
|
|
189
|
+
- greengrocer
|
|
190
|
+
- fast_food
|
|
191
|
+
|
|
192
|
+
# ============================================================
|
|
193
|
+
# TRANSPORT
|
|
194
|
+
# ============================================================
|
|
195
|
+
|
|
196
|
+
transport:
|
|
197
|
+
osm:
|
|
198
|
+
aeroway:
|
|
199
|
+
- aerodrome
|
|
200
|
+
- runway
|
|
201
|
+
- taxiway
|
|
202
|
+
- apron
|
|
203
|
+
- terminal
|
|
204
|
+
public_transport:
|
|
205
|
+
- station
|
|
206
|
+
- stop_position
|
|
207
|
+
- platform
|
|
208
|
+
|
|
209
|
+
# ============================================================
|
|
210
|
+
# NATURAL FEATURES
|
|
211
|
+
# ============================================================
|
|
212
|
+
|
|
213
|
+
natural:
|
|
214
|
+
natural_earth:
|
|
215
|
+
featurecla:
|
|
216
|
+
- mountain
|
|
217
|
+
- peak
|
|
218
|
+
- hill
|
|
219
|
+
- volcano
|
|
220
|
+
|
|
221
|
+
osm:
|
|
222
|
+
natural:
|
|
223
|
+
- peak
|
|
224
|
+
- hill
|
|
225
|
+
- volcano
|
|
226
|
+
- ridge
|
|
227
|
+
- cliff
|
|
228
|
+
- dune
|
|
229
|
+
|
|
230
|
+
# ============================================================
|
|
231
|
+
# LANDUSE
|
|
232
|
+
# ============================================================
|
|
233
|
+
|
|
234
|
+
landuse:
|
|
235
|
+
osm:
|
|
236
|
+
landuse:
|
|
237
|
+
- residential
|
|
238
|
+
- commercial
|
|
239
|
+
- industrial
|
|
240
|
+
- retail
|
|
241
|
+
- farmland
|
|
242
|
+
- forest
|
|
243
|
+
- grass
|
|
244
|
+
- meadow
|
|
245
|
+
- quarry
|
|
246
|
+
- cemetery
|
|
247
|
+
- landfill
|
|
248
|
+
- recreation_ground
|
|
249
|
+
roads:
|
|
250
|
+
osm_geofabrik:
|
|
251
|
+
fclass:
|
|
252
|
+
- motorway
|
|
253
|
+
- motorway_link
|
|
254
|
+
- trunk
|
|
255
|
+
- trunk_link
|
|
256
|
+
- primary
|
|
257
|
+
- primary_link
|
|
258
|
+
- secondary
|
|
259
|
+
- secondary_link
|
|
260
|
+
- tertiary
|
|
261
|
+
- tertiary_link
|
|
262
|
+
- residential
|
|
263
|
+
- unclassified
|
|
264
|
+
- service
|
|
265
|
+
- living_street
|
|
266
|
+
- pedestrian
|
|
267
|
+
- road
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
require "json"
|
|
2
|
+
|
|
3
|
+
module GD
|
|
4
|
+
module GIS
|
|
5
|
+
class PathSampler
|
|
6
|
+
def initialize(coords)
|
|
7
|
+
@coords = coords
|
|
8
|
+
@segments = []
|
|
9
|
+
@total = 0.0
|
|
10
|
+
|
|
11
|
+
coords.each_cons(2) do |a,b|
|
|
12
|
+
d = haversine(a[0],a[1], b[0],b[1])
|
|
13
|
+
@segments << [a,b,d]
|
|
14
|
+
@total += d
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def point_at(t)
|
|
19
|
+
target = t * @total
|
|
20
|
+
acc = 0.0
|
|
21
|
+
|
|
22
|
+
@segments.each do |a,b,d|
|
|
23
|
+
return interpolate(a,b,(target-acc)/d) if acc + d >= target
|
|
24
|
+
acc += d
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
@coords.last
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def interpolate(a,b,t)
|
|
31
|
+
[
|
|
32
|
+
a[0] + (b[0]-a[0])*t,
|
|
33
|
+
a[1] + (b[1]-a[1])*t
|
|
34
|
+
]
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def haversine(lon1,lat1,lon2,lat2)
|
|
38
|
+
r = 6371000
|
|
39
|
+
dlat = (lat2-lat1) * Math::PI/180
|
|
40
|
+
dlon = (lon2-lon1) * Math::PI/180
|
|
41
|
+
a = Math.sin(dlat/2)**2 +
|
|
42
|
+
Math.cos(lat1*Math::PI/180)*Math.cos(lat2*Math::PI/180)*
|
|
43
|
+
Math.sin(dlon/2)**2
|
|
44
|
+
2 * r * Math.atan2(Math.sqrt(a), Math.sqrt(1-a))
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def self.from_geojson(path)
|
|
48
|
+
data = JSON.parse(File.read(path))
|
|
49
|
+
|
|
50
|
+
coords =
|
|
51
|
+
if data["features"]
|
|
52
|
+
data["features"][0]["geometry"]["coordinates"]
|
|
53
|
+
|
|
54
|
+
elsif data["paths"]
|
|
55
|
+
data["paths"][0]["points"]["coordinates"]
|
|
56
|
+
|
|
57
|
+
elsif data["routes"]
|
|
58
|
+
data["routes"][0]["geometry"]["coordinates"]
|
|
59
|
+
|
|
60
|
+
else
|
|
61
|
+
raise "Formato de ruta desconocido en #{path}"
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
new(coords)
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
data/lib/gd/gis.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: libgd-gis
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.2.
|
|
4
|
+
version: 0.2.7.pre.alpha.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Germán Alberto Giménez Silva
|
|
@@ -15,20 +15,20 @@ dependencies:
|
|
|
15
15
|
requirements:
|
|
16
16
|
- - "~>"
|
|
17
17
|
- !ruby/object:Gem::Version
|
|
18
|
-
version: 0.2.
|
|
18
|
+
version: 0.2.3
|
|
19
19
|
- - ">="
|
|
20
20
|
- !ruby/object:Gem::Version
|
|
21
|
-
version: 0.2.
|
|
21
|
+
version: 0.2.3
|
|
22
22
|
type: :runtime
|
|
23
23
|
prerelease: false
|
|
24
24
|
version_requirements: !ruby/object:Gem::Requirement
|
|
25
25
|
requirements:
|
|
26
26
|
- - "~>"
|
|
27
27
|
- !ruby/object:Gem::Version
|
|
28
|
-
version: 0.2.
|
|
28
|
+
version: 0.2.3
|
|
29
29
|
- - ">="
|
|
30
30
|
- !ruby/object:Gem::Version
|
|
31
|
-
version: 0.2.
|
|
31
|
+
version: 0.2.3
|
|
32
32
|
description: A native GIS raster engine for Ruby built on libgd. Render maps, GeoJSON,
|
|
33
33
|
heatmaps and tiles.
|
|
34
34
|
email:
|
|
@@ -41,13 +41,22 @@ files:
|
|
|
41
41
|
- lib/gd/gis.rb
|
|
42
42
|
- lib/gd/gis/basemap.rb
|
|
43
43
|
- lib/gd/gis/classifier.rb
|
|
44
|
+
- lib/gd/gis/crs_normalizer.rb
|
|
44
45
|
- lib/gd/gis/feature.rb
|
|
45
46
|
- lib/gd/gis/geometry.rb
|
|
47
|
+
- lib/gd/gis/input.rb
|
|
48
|
+
- lib/gd/gis/input/detector.rb
|
|
49
|
+
- lib/gd/gis/input/geojson.rb
|
|
50
|
+
- lib/gd/gis/input/kml.rb
|
|
51
|
+
- lib/gd/gis/input/shapefile.rb
|
|
46
52
|
- lib/gd/gis/layer_geojson.rb
|
|
47
53
|
- lib/gd/gis/layer_lines.rb
|
|
48
54
|
- lib/gd/gis/layer_points.rb
|
|
49
55
|
- lib/gd/gis/layer_polygons.rb
|
|
50
56
|
- lib/gd/gis/map.rb
|
|
57
|
+
- lib/gd/gis/ontology.rb
|
|
58
|
+
- lib/gd/gis/ontology.yml
|
|
59
|
+
- lib/gd/gis/path_sampler.rb
|
|
51
60
|
- lib/gd/gis/projection.rb
|
|
52
61
|
- lib/gd/gis/style.rb
|
|
53
62
|
- lib/gd/gis/style/dark.rb
|
|
@@ -72,7 +81,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
72
81
|
- !ruby/object:Gem::Version
|
|
73
82
|
version: '0'
|
|
74
83
|
requirements: []
|
|
75
|
-
rubygems_version: 4.0.
|
|
84
|
+
rubygems_version: 4.0.4
|
|
76
85
|
specification_version: 4
|
|
77
86
|
summary: Geospatial raster rendering for Ruby using libgd
|
|
78
87
|
test_files: []
|