Class: GD::GIS::Basemap
- Inherits:
-
Object
- Object
- GD::GIS::Basemap
- Defined in:
- lib/gd/gis/basemap.rb
Overview
Fetches and manages raster basemap tiles using the XYZ Web Mercator tiling scheme.
A Basemap is responsible for:
-
Converting geographic coordinates to tile coordinates
-
Downloading raster tiles from public tile providers
-
Caching tiles on disk
-
Exposing pixel origins for map composition
All tiles are assumed to be 256×256 pixels.
Constant Summary collapse
- TILE_SIZE =
Tile size in pixels (Web Mercator standard)
256
Instance Attribute Summary collapse
-
#origin_x ⇒ Integer
readonly
Pixel X origin of the basemap.
-
#origin_y ⇒ Integer
readonly
Pixel Y origin of the basemap.
Instance Method Summary collapse
-
#fetch_tiles ⇒ Array
Downloads all tiles required to cover the bounding box.
-
#initialize(zoom, bbox, provider = :carto_light) ⇒ Basemap
constructor
Creates a new basemap tile source.
-
#lat2tile(lat) ⇒ Integer
Converts latitude to tile Y coordinate.
-
#lon2tile(lon) ⇒ Integer
Converts longitude to tile X coordinate.
-
#url(z, x, y, style = :osm) ⇒ String
Builds a tile URL for a given provider and tile coordinate.
Constructor Details
#initialize(zoom, bbox, provider = :carto_light) ⇒ Basemap
Creates a new basemap tile source.
39 40 41 42 43 |
# File 'lib/gd/gis/basemap.rb', line 39 def initialize(zoom, bbox, provider = :carto_light) @zoom = zoom @bbox = bbox @provider = provider end |
Instance Attribute Details
#origin_x ⇒ Integer (readonly)
Returns pixel X origin of the basemap.
29 30 31 |
# File 'lib/gd/gis/basemap.rb', line 29 def origin_x @origin_x end |
#origin_y ⇒ Integer (readonly)
Returns pixel Y origin of the basemap.
32 33 34 |
# File 'lib/gd/gis/basemap.rb', line 32 def origin_y @origin_y end |
Instance Method Details
#fetch_tiles ⇒ Array
Downloads all tiles required to cover the bounding box.
Tiles are cached on disk under ‘tmp/tiles`.
167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 |
# File 'lib/gd/gis/basemap.rb', line 167 def fetch_tiles west, south, east, north = @bbox x_min = lon2tile(west) x_max = lon2tile(east) y_min = lat2tile(north) y_max = lat2tile(south) @x_min = x_min @y_min = y_min @origin_x = x_min * TILE_SIZE @origin_y = y_min * TILE_SIZE FileUtils.mkdir_p("tmp/tiles") tiles = [] (x_min..x_max).each do |x| (y_min..y_max).each do |y| path = nil if File.exist?("tmp/tiles/#{@provider}_#{@zoom}_#{x}_#{y}.png") || File.exist?("tmp/tiles/#{@provider}_#{@zoom}_#{x}_#{y}.jpg") path = if File.exist?("tmp/tiles/#{@provider}_#{@zoom}_#{x}_#{y}.png") "tmp/tiles/#{@provider}_#{@zoom}_#{x}_#{y}.png" else "tmp/tiles/#{@provider}_#{@zoom}_#{x}_#{y}.jpg" end else uri = URI(url(@zoom, x, y, @provider)) Net::HTTP.start(uri.host, uri.port, use_ssl: true) do |http| req = Net::HTTP::Get.new(uri) req["User-Agent"] = "libgd-gis/0.1 (Ruby)" res = http.request(req) raise "Tile fetch failed #{res.code}" unless res.code == "200" content_type = res["content-type"] ext = if content_type&.include?("png") "png" elsif content_type&.include?("jpeg") || content_type&.include?("jpg") "jpg" else raise "Unsupported tile type: #{content_type}" end path = "tmp/tiles/#{@provider}_#{@zoom}_#{x}_#{y}.#{ext}" File.binwrite(path, res.body) end end tiles << [x, y, path] end end [tiles, x_min, y_min] end |
#lat2tile(lat) ⇒ Integer
Converts latitude to tile Y coordinate.
151 152 153 154 |
# File 'lib/gd/gis/basemap.rb', line 151 def lat2tile(lat) rad = lat * Math::PI / 180 ((1 - (Math.log(Math.tan(rad) + (1 / Math.cos(rad))) / Math::PI)) / 2 * (2**@zoom)).floor end |
#lon2tile(lon) ⇒ Integer
Converts longitude to tile X coordinate.
143 144 145 |
# File 'lib/gd/gis/basemap.rb', line 143 def lon2tile(lon) ((lon + 180.0) / 360.0 * (2**@zoom)).floor end |
#url(z, x, y, style = :osm) ⇒ String
Builds a tile URL for a given provider and tile coordinate.
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 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 |
# File 'lib/gd/gis/basemap.rb', line 53 def url(z, x, y, style = :osm) case style # ============================== # OpenStreetMap # ============================== when :osm "https://tile.openstreetmap.org/#{z}/#{x}/#{y}.png" when :osm_hot "https://tile.openstreetmap.fr/hot/#{z}/#{x}/#{y}.png" when :osm_fr "https://a.tile.openstreetmap.fr/osmfr/#{z}/#{x}/#{y}.png" # ============================== # CARTO # ============================== when :carto_light "https://a.basemaps.cartocdn.com/light_all/#{z}/#{x}/#{y}.png" when :carto_light_nolabels "https://a.basemaps.cartocdn.com/light_nolabels/#{z}/#{x}/#{y}.png" when :carto_dark "https://a.basemaps.cartocdn.com/dark_all/#{z}/#{x}/#{y}.png" when :carto_dark_nolabels "https://a.basemaps.cartocdn.com/dark_nolabels/#{z}/#{x}/#{y}.png" # ============================== # ESRI / ArcGIS (Satellite, terrain, hybrid) # ============================== when :esri_satellite "https://services.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/#{z}/#{y}/#{x}" when :esri_streets "https://services.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer/tile/#{z}/#{y}/#{x}" when :esri_terrain "https://services.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer/tile/#{z}/#{y}/#{x}" # ============================== # STAMEN 503 # ============================== when :stamen_toner "https://stamen-tiles.a.ssl.fastly.net/toner/#{z}/#{x}/#{y}.png" when :stamen_toner_lite "https://stamen-tiles.a.ssl.fastly.net/toner-lite/#{z}/#{x}/#{y}.png" when :stamen_terrain "https://stamen-tiles.a.ssl.fastly.net/terrain/#{z}/#{x}/#{y}.png" when :stamen_watercolor "https://stamen-tiles.a.ssl.fastly.net/watercolor/#{z}/#{x}/#{y}.jpg" # ============================== # OpenTopoMap # ============================== when :topo "https://a.tile.opentopomap.org/#{z}/#{x}/#{y}.png" # ============================== # Wikimedia 403 # ============================== when :wikimedia "https://maps.wikimedia.org/osm-intl/#{z}/#{x}/#{y}.png" # ============================== # OpenRailwayMap # ============================== when :railway "https://tiles.openrailwaymap.org/standard/#{z}/#{x}/#{y}.png" # ============================== # CyclOSM # ============================== when :cyclosm "https://a.tile-cyclosm.openstreetmap.fr/cyclosm/#{z}/#{x}/#{y}.png" else raise "Unknown basemap style: #{style}" end end |