map_view 0.0.1a → 0.1.0.pre.alpha

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.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +0 -188
  3. data/Rakefile +7 -0
  4. data/app/controllers/map_view/maps_controller.rb +20 -0
  5. data/config/initializers/map_view.rb +4 -0
  6. data/config/routes.rb +4 -0
  7. data/lib/generators/map_view/install_generator.rb +65 -0
  8. data/lib/generators/map_view/templates/default_styles.yml +45 -0
  9. data/lib/generators/map_view/templates/initializer.rb +14 -0
  10. data/lib/generators/map_view/templates/maps_controller.rb +15 -0
  11. data/lib/map_view/config.rb +67 -0
  12. data/lib/map_view/engine.rb +39 -0
  13. data/lib/map_view/version.rb +4 -0
  14. data/lib/map_view/view_helper.rb +116 -0
  15. data/lib/map_view.rb +24 -0
  16. metadata +83 -39
  17. data/lib/gd/gis/basemap.rb +0 -178
  18. data/lib/gd/gis/classifier.rb +0 -57
  19. data/lib/gd/gis/color_helpers.rb +0 -65
  20. data/lib/gd/gis/crs_normalizer.rb +0 -57
  21. data/lib/gd/gis/feature.rb +0 -153
  22. data/lib/gd/gis/geometry.rb +0 -235
  23. data/lib/gd/gis/input/detector.rb +0 -34
  24. data/lib/gd/gis/layer_geojson.rb +0 -66
  25. data/lib/gd/gis/layer_lines.rb +0 -44
  26. data/lib/gd/gis/layer_points.rb +0 -78
  27. data/lib/gd/gis/layer_polygons.rb +0 -54
  28. data/lib/gd/gis/map.rb +0 -370
  29. data/lib/gd/gis/middleware.rb +0 -89
  30. data/lib/gd/gis/ontology.rb +0 -26
  31. data/lib/gd/gis/ontology.yml +0 -28
  32. data/lib/gd/gis/projection.rb +0 -39
  33. data/lib/gd/gis/style.rb +0 -45
  34. data/lib/gd/gis.rb +0 -15
  35. data/lib/libgd_gis.rb +0 -44
  36. /data/{lib/gd/gis/input/geojson.rb → MIT-LICENSE} +0 -0
  37. /data/{lib/gd/gis/input/kml.rb → app/assets/stylesheets/map_view.css} +0 -0
  38. /data/{lib/gd/gis/input/shapefile.rb → app/views/map_view/_map.html.erb} +0 -0
metadata CHANGED
@@ -1,67 +1,111 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: map_view
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1a
4
+ version: 0.1.0.pre.alpha
5
5
  platform: ruby
6
6
  authors:
7
- - Germán Alberto Giménez Silva
7
+ - Your Name
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-01-19 00:00:00.000000000 Z
11
+ date: 2026-04-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: ruby-libgd
14
+ name: rails
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - "~>"
17
+ - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: 0.2.3
19
+ version: '6.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
20
24
  - - ">="
21
25
  - !ruby/object:Gem::Version
22
- version: 0.2.3
26
+ version: '6.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: libgd-gis
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 0.4.5
23
34
  type: :runtime
24
35
  prerelease: false
25
36
  version_requirements: !ruby/object:Gem::Requirement
26
37
  requirements:
27
38
  - - "~>"
28
39
  - !ruby/object:Gem::Version
29
- version: 0.2.3
30
- - - ">="
40
+ version: 0.4.5
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
31
53
  - !ruby/object:Gem::Version
32
- version: 0.2.3
33
- description: A native GIS raster engine for Ruby built on libgd. Render maps, GeoJSON,
34
- heatmaps and tiles.
54
+ version: '3.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '13.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '13.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: pry
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '0.14'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '0.14'
83
+ description: Simple Rails integration for libgd-gis. Provides view helpers to render
84
+ maps from GeoJSON data.
35
85
  email:
36
- - ggerman@gmail.com
86
+ - your@email.com
37
87
  executables: []
38
88
  extensions: []
39
89
  extra_rdoc_files: []
40
90
  files:
91
+ - MIT-LICENSE
41
92
  - README.md
42
- - lib/gd/gis.rb
43
- - lib/gd/gis/basemap.rb
44
- - lib/gd/gis/classifier.rb
45
- - lib/gd/gis/color_helpers.rb
46
- - lib/gd/gis/crs_normalizer.rb
47
- - lib/gd/gis/feature.rb
48
- - lib/gd/gis/geometry.rb
49
- - lib/gd/gis/input/detector.rb
50
- - lib/gd/gis/input/geojson.rb
51
- - lib/gd/gis/input/kml.rb
52
- - lib/gd/gis/input/shapefile.rb
53
- - lib/gd/gis/layer_geojson.rb
54
- - lib/gd/gis/layer_lines.rb
55
- - lib/gd/gis/layer_points.rb
56
- - lib/gd/gis/layer_polygons.rb
57
- - lib/gd/gis/map.rb
58
- - lib/gd/gis/middleware.rb
59
- - lib/gd/gis/ontology.rb
60
- - lib/gd/gis/ontology.yml
61
- - lib/gd/gis/projection.rb
62
- - lib/gd/gis/style.rb
63
- - lib/libgd_gis.rb
64
- homepage: https://github.com/ggerman/libgd-gis
93
+ - Rakefile
94
+ - app/assets/stylesheets/map_view.css
95
+ - app/controllers/map_view/maps_controller.rb
96
+ - app/views/map_view/_map.html.erb
97
+ - config/initializers/map_view.rb
98
+ - config/routes.rb
99
+ - lib/generators/map_view/install_generator.rb
100
+ - lib/generators/map_view/templates/default_styles.yml
101
+ - lib/generators/map_view/templates/initializer.rb
102
+ - lib/generators/map_view/templates/maps_controller.rb
103
+ - lib/map_view.rb
104
+ - lib/map_view/config.rb
105
+ - lib/map_view/engine.rb
106
+ - lib/map_view/version.rb
107
+ - lib/map_view/view_helper.rb
108
+ homepage: https://github.com/yourusername/map_view
65
109
  licenses:
66
110
  - MIT
67
111
  metadata: {}
@@ -73,15 +117,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
73
117
  requirements:
74
118
  - - ">="
75
119
  - !ruby/object:Gem::Version
76
- version: '3.3'
120
+ version: '0'
77
121
  required_rubygems_version: !ruby/object:Gem::Requirement
78
122
  requirements:
79
123
  - - ">="
80
124
  - !ruby/object:Gem::Version
81
125
  version: '0'
82
126
  requirements: []
83
- rubygems_version: 3.5.22
127
+ rubygems_version: 3.5.3
84
128
  signing_key:
85
129
  specification_version: 4
86
- summary: Geospatial raster rendering for Rails using libgd
130
+ summary: Rails view helper for GD::GIS maps
87
131
  test_files: []
@@ -1,178 +0,0 @@
1
- require "net/http"
2
- require "fileutils"
3
-
4
- module GD
5
- module GIS
6
- class Basemap
7
- TILE_SIZE = 256
8
- attr_reader :origin_x, :origin_y
9
-
10
- def initialize(zoom, bbox, provider=:carto_light)
11
- @zoom = zoom
12
- @bbox = bbox
13
- @provider = provider
14
- end
15
-
16
- def url(z, x, y, style = :osm)
17
- case style
18
-
19
- # ==============================
20
- # OpenStreetMap
21
- # ==============================
22
- when :osm
23
- "https://tile.openstreetmap.org/#{z}/#{x}/#{y}.png"
24
-
25
- when :osm_hot
26
- "https://tile.openstreetmap.fr/hot/#{z}/#{x}/#{y}.png"
27
-
28
- when :osm_fr
29
- "https://a.tile.openstreetmap.fr/osmfr/#{z}/#{x}/#{y}.png"
30
-
31
- # ==============================
32
- # CARTO
33
- # ==============================
34
- when :carto_light
35
- "https://a.basemaps.cartocdn.com/light_all/#{z}/#{x}/#{y}.png"
36
-
37
- when :carto_light_nolabels
38
- "https://a.basemaps.cartocdn.com/light_nolabels/#{z}/#{x}/#{y}.png"
39
-
40
- when :carto_dark
41
- "https://a.basemaps.cartocdn.com/dark_all/#{z}/#{x}/#{y}.png"
42
-
43
- when :carto_dark_nolabels
44
- "https://a.basemaps.cartocdn.com/dark_nolabels/#{z}/#{x}/#{y}.png"
45
-
46
- # ==============================
47
- # ESRI / ArcGIS (Satellite, terrain, hybrid)
48
- # ==============================
49
- when :esri_satellite
50
- "https://services.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/#{z}/#{y}/#{x}"
51
-
52
- when :esri_streets
53
- "https://services.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer/tile/#{z}/#{y}/#{x}"
54
-
55
- when :esri_terrain
56
- "https://services.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer/tile/#{z}/#{y}/#{x}"
57
-
58
- when :esri_hybrid
59
- "https://services.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/#{z}/#{y}/#{x}"
60
-
61
- # ==============================
62
- # STAMEN 503
63
- # ==============================
64
- when :stamen_toner
65
- "https://stamen-tiles.a.ssl.fastly.net/toner/#{z}/#{x}/#{y}.png"
66
-
67
- when :stamen_toner_lite
68
- "https://stamen-tiles.a.ssl.fastly.net/toner-lite/#{z}/#{x}/#{y}.png"
69
-
70
- when :stamen_terrain
71
- "https://stamen-tiles.a.ssl.fastly.net/terrain/#{z}/#{x}/#{y}.png"
72
-
73
- when :stamen_watercolor
74
- "https://stamen-tiles.a.ssl.fastly.net/watercolor/#{z}/#{x}/#{y}.jpg"
75
-
76
- # ==============================
77
- # OpenTopoMap
78
- # ==============================
79
- when :topo
80
- "https://a.tile.opentopomap.org/#{z}/#{x}/#{y}.png"
81
-
82
- # ==============================
83
- # Wikimedia 403
84
- # ==============================
85
- when :wikimedia
86
- "https://maps.wikimedia.org/osm-intl/#{z}/#{x}/#{y}.png"
87
-
88
- # ==============================
89
- # OpenRailwayMap
90
- # ==============================
91
- when :railway
92
- "https://tiles.openrailwaymap.org/standard/#{z}/#{x}/#{y}.png"
93
-
94
- # ==============================
95
- # CyclOSM
96
- # ==============================
97
- when :cyclosm
98
- "https://a.tile-cyclosm.openstreetmap.fr/cyclosm/#{z}/#{x}/#{y}.png"
99
-
100
- else
101
- raise "Unknown basemap style: #{style}"
102
- end
103
- end
104
-
105
- def lon2tile(lon)
106
- ((lon + 180.0) / 360.0 * (2 ** @zoom)).floor
107
- end
108
-
109
- def lat2tile(lat)
110
- rad = lat * Math::PI / 180
111
- ((1 - Math.log(Math.tan(rad) + 1 / Math.cos(rad)) / Math::PI) / 2 * (2 ** @zoom)).floor
112
- end
113
-
114
- def fetch_tiles
115
- west, south, east, north = @bbox
116
-
117
- x_min = lon2tile(west)
118
- x_max = lon2tile(east)
119
- y_min = lat2tile(north)
120
- y_max = lat2tile(south)
121
-
122
- @x_min = x_min
123
- @y_min = y_min
124
-
125
- @origin_x = x_min * TILE_SIZE
126
- @origin_y = y_min * TILE_SIZE
127
-
128
- FileUtils.mkdir_p("tmp/tiles")
129
-
130
- tiles = []
131
-
132
- (x_min..x_max).each do |x|
133
- (y_min..y_max).each do |y|
134
- path = nil
135
-
136
- unless File.exist?("tmp/tiles/#{@provider}_#{@zoom}_#{x}_#{y}.png") ||
137
- File.exist?("tmp/tiles/#{@provider}_#{@zoom}_#{x}_#{y}.jpg")
138
-
139
- uri = URI(url(@zoom, x, y, @provider))
140
-
141
- Net::HTTP.start(uri.host, uri.port, use_ssl: true) do |http|
142
- req = Net::HTTP::Get.new(uri)
143
- req["User-Agent"] = "libgd-gis/0.1 (Ruby)"
144
-
145
- res = http.request(req)
146
- raise "Tile fetch failed #{res.code}" unless res.code == "200"
147
-
148
- content_type = res["content-type"]
149
-
150
- ext =
151
- if content_type&.include?("png")
152
- "png"
153
- elsif content_type&.include?("jpeg") || content_type&.include?("jpg")
154
- "jpg"
155
- else
156
- raise "Unsupported tile type: #{content_type}"
157
- end
158
-
159
- path = "tmp/tiles/#{@provider}_#{@zoom}_#{x}_#{y}.#{ext}"
160
- File.binwrite(path, res.body)
161
- end
162
- else
163
- if File.exist?("tmp/tiles/#{@provider}_#{@zoom}_#{x}_#{y}.png")
164
- path = "tmp/tiles/#{@provider}_#{@zoom}_#{x}_#{y}.png"
165
- else
166
- path = "tmp/tiles/#{@provider}_#{@zoom}_#{x}_#{y}.jpg"
167
- end
168
- end
169
-
170
- tiles << [x,y,path]
171
- end
172
- end
173
-
174
- [tiles, x_min, y_min]
175
- end
176
- end
177
- end
178
- end
@@ -1,57 +0,0 @@
1
- module GD
2
- module GIS
3
- class Classifier
4
- def self.road(feature)
5
- tags = feature.properties || {}
6
-
7
- case tags["highway"]
8
- when "motorway", "trunk"
9
- :motorway
10
- when "primary", "primary_link"
11
- :primary
12
- when "secondary", "secondary_link"
13
- :secondary
14
- when "tertiary"
15
- :street
16
- when "residential", "living_street"
17
- :street
18
- when "service", "track"
19
- :minor
20
- else
21
- nil
22
- end
23
- end
24
-
25
- def self.water?(feature)
26
- p = feature.properties
27
-
28
- p["waterway"] ||
29
- p["natural"] == "water" ||
30
- p["fclass"] == "river" ||
31
- p["fclass"] == "stream"
32
- end
33
-
34
- def self.rail?(feature)
35
- tags = feature.properties || {}
36
- tags["railway"]
37
- end
38
-
39
- def self.park?(feature)
40
- tags = feature.properties || {}
41
- %w[park recreation_ground garden].include?(tags["leisure"]) ||
42
- %w[park grass forest].include?(tags["landuse"])
43
- end
44
-
45
- def self.water_kind(feature)
46
- p = feature.properties
47
-
48
- case p["waterway"] || p["fclass"]
49
- when "river" then :river
50
- when "stream" then :stream
51
- else :minor
52
- end
53
- end
54
-
55
- end
56
- end
57
- end
@@ -1,65 +0,0 @@
1
- module GD
2
- module GIS
3
- module ColorHelpers
4
- # --------------------------------------------------
5
- # Random RGB color
6
- # --------------------------------------------------
7
- def self.random_rgb(min: 0, max: 255)
8
- GD::Color.rgb(
9
- rand(min..max),
10
- rand(min..max),
11
- rand(min..max)
12
- )
13
- end
14
-
15
- # --------------------------------------------------
16
- # Random RGBA color
17
- # --------------------------------------------------
18
- def self.random_rgba(min: 0, max: 255, alpha: nil)
19
- GD::Color.rgba(
20
- rand(min..max),
21
- rand(min..max),
22
- rand(min..max),
23
- alpha || rand(50..255)
24
- )
25
- end
26
-
27
- # --------------------------------------------------
28
- # Random vivid color (avoid gray/mud)
29
- # --------------------------------------------------
30
- def self.random_vivid
31
- h = rand
32
- s = rand(0.6..1.0)
33
- v = rand(0.7..1.0)
34
-
35
- r, g, b = hsv_to_rgb(h, s, v)
36
- GD::Color.rgb(r, g, b)
37
- end
38
-
39
- # --------------------------------------------------
40
- # HSV → RGB
41
- # --------------------------------------------------
42
- def self.hsv_to_rgb(h, s, v)
43
- i = (h * 6).floor
44
- f = h * 6 - i
45
- p = v * (1 - s)
46
- q = v * (1 - f * s)
47
- t = v * (1 - (1 - f) * s)
48
-
49
- r, g, b =
50
- case i % 6
51
- when 0 then [v, t, p]
52
- when 1 then [q, v, p]
53
- when 2 then [p, v, t]
54
- when 3 then [p, q, v]
55
- when 4 then [t, p, v]
56
- when 5 then [v, p, q]
57
- end
58
-
59
- [(r * 255).to_i, (g * 255).to_i, (b * 255).to_i]
60
- end
61
-
62
- end
63
- end
64
- end
65
-
@@ -1,57 +0,0 @@
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
@@ -1,153 +0,0 @@
1
- module GD
2
- module GIS
3
- class Feature
4
- attr_reader :geometry, :properties, :layer
5
-
6
- def initialize(geometry, properties, layer = nil)
7
- @geometry = geometry
8
- @properties = properties || {}
9
- @layer = layer
10
- end
11
-
12
- # -------------------------------------------------
13
- # Main draw entry point
14
- # -------------------------------------------------
15
- def draw(img, projection, color, width, layer = nil)
16
- case geometry["type"]
17
- when "Polygon"
18
- if layer == :water
19
- draw_polygon_outline(img, projection, geometry["coordinates"], color, width)
20
- elsif layer.is_a?(Hash)
21
- draw_polygon_styled(img, projection, geometry["coordinates"], layer)
22
- else
23
- draw_polygon(img, projection, geometry["coordinates"], color)
24
- end
25
- when "MultiPolygon"
26
- geometry["coordinates"].each do |poly|
27
- if layer == :water
28
- draw_polygon_outline(img, projection, poly, color, width)
29
- elsif layer.is_a?(Hash)
30
- draw_polygon_styled(img, projection, poly, layer)
31
- else
32
- draw_polygon(img, projection, poly, color)
33
- end
34
- end
35
- when "LineString", "MultiLineString"
36
- draw_lines(img, projection, geometry["coordinates"], color, width)
37
- end
38
- end
39
-
40
- # -------------------------------------------------
41
- # Styled polygon rendering (fill + stroke)
42
- # -------------------------------------------------
43
- def draw_polygon_styled(img, projection, rings, style)
44
- fill = style[:fill] ? GD::Color.rgb(*style[:fill]) : nil
45
- stroke = style[:stroke] ? GD::Color.rgb(*style[:stroke]) : nil
46
-
47
- rings.each do |ring|
48
- pts = ring.map do |lon,lat|
49
- x,y = projection.call(lon,lat)
50
- next if x.nil? || y.nil?
51
- [x.to_i, y.to_i]
52
- end.compact
53
-
54
- pts = pts.chunk_while { |a,b| a == b }.map(&:first)
55
- next if pts.length < 3
56
-
57
- img.filled_polygon(pts, fill) if fill
58
-
59
- if stroke
60
- pts.each_cons(2) { |a,b| img.line(a[0],a[1], b[0],b[1], stroke) }
61
- img.line(pts.last[0], pts.last[1], pts.first[0], pts.first[1], stroke)
62
- end
63
- end
64
- end
65
-
66
- # -------------------------------------------------
67
- # Polygon outline (used for water)
68
- # -------------------------------------------------
69
- def draw_polygon_outline(img, projection, rings, color, width)
70
- return if color.nil?
71
-
72
- rings.each do |ring|
73
- pts = ring.map do |lon, lat|
74
- x, y = projection.call(lon, lat)
75
- [x.to_i, y.to_i] if x && y
76
- end.compact
77
-
78
- next if pts.size < 2
79
-
80
- img.lines(pts, color, width)
81
- end
82
- end
83
-
84
- # -------------------------------------------------
85
- # Legacy filled polygon (single color)
86
- # -------------------------------------------------
87
- def draw_polygon(img, projection, rings, color)
88
- return if color.nil?
89
-
90
- rings.each do |ring|
91
- pts = ring.map do |lon,lat|
92
- x,y = projection.call(lon,lat)
93
- next if x.nil? || y.nil?
94
- [x.to_i, y.to_i]
95
- end.compact
96
-
97
- pts = pts.chunk_while { |a,b| a == b }.map(&:first)
98
- next if pts.length < 3
99
-
100
- img.filled_polygon(pts, color)
101
- end
102
- end
103
-
104
- # -------------------------------------------------
105
- # Lines
106
- # -------------------------------------------------
107
- def draw_lines(img, projection, coords, color, width)
108
- return if color.nil?
109
-
110
- if coords.first.is_a?(Array) && coords.first.first.is_a?(Array)
111
- coords.each { |line| draw_line(img, projection, line, color, width) }
112
- else
113
- draw_line(img, projection, coords, color, width)
114
- end
115
- end
116
-
117
- def draw_line(img, projection, coords, color, width)
118
- return if color.nil?
119
-
120
- coords.each_cons(2) do |(lon1,lat1),(lon2,lat2)|
121
- x1,y1 = projection.call(lon1,lat1)
122
- x2,y2 = projection.call(lon2,lat2)
123
- img.line(x1, y1, x2, y2, color, thickness: width)
124
- end
125
- end
126
-
127
- # -------------------------------------------------
128
- # Metadata helpers
129
- # -------------------------------------------------
130
- def label
131
- properties["name:ja"] || properties["name"]
132
- end
133
-
134
- def centroid
135
- pts = []
136
-
137
- case geometry["type"]
138
- when "LineString"
139
- pts = geometry["coordinates"]
140
- when "MultiLineString"
141
- pts = geometry["coordinates"].flatten(1)
142
- end
143
-
144
- return nil if pts.empty?
145
-
146
- lon = pts.map(&:first).sum / pts.size
147
- lat = pts.map(&:last).sum / pts.size
148
-
149
- [lon, lat]
150
- end
151
- end
152
- end
153
- end