libgd-gis 0.1.0
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 +7 -0
- data/README.md +127 -0
- data/examples/argentina.png +0 -0
- data/examples/argentina.rb +82 -0
- data/examples/argentina_con_localidades.rb +89 -0
- data/examples/argentina_museos.rb +70 -0
- data/examples/flag.png +0 -0
- data/examples/hydro.png +0 -0
- data/examples/hydro_plants.geojson +25 -0
- data/examples/localidades.json +1 -0
- data/examples/museos.png +0 -0
- data/examples/museos_datosabiertos.csv +1183 -0
- data/examples/output/argentina.png +0 -0
- data/examples/output/argentina_localidades.png +0 -0
- data/examples/output/argentina_museos.png +0 -0
- data/examples/output/tanzania_hydro.png +0 -0
- data/examples/render_tile.rb +31 -0
- data/examples/tanzania.rb +32 -0
- data/examples/target.rb +19 -0
- data/examples/tmp/tiles/7_27_43.png +0 -0
- data/examples/tmp/tiles/7_27_44.png +0 -0
- data/examples/tmp/tiles/7_27_45.png +0 -0
- data/examples/tmp/tiles/7_27_46.png +0 -0
- data/examples/tmp/tiles/7_27_47.png +0 -0
- data/examples/tmp/tiles/7_27_48.png +0 -0
- data/examples/tmp/tiles/7_27_49.png +0 -0
- data/examples/tmp/tiles/7_27_50.png +0 -0
- data/examples/tmp/tiles/7_28_43.png +0 -0
- data/examples/tmp/tiles/7_28_44.png +0 -0
- data/examples/tmp/tiles/7_28_45.png +0 -0
- data/examples/tmp/tiles/7_28_46.png +0 -0
- data/examples/tmp/tiles/7_28_47.png +0 -0
- data/examples/tmp/tiles/7_28_48.png +0 -0
- data/examples/tmp/tiles/7_28_49.png +0 -0
- data/examples/tmp/tiles/7_28_50.png +0 -0
- data/examples/tmp/tiles/7_29_43.png +0 -0
- data/examples/tmp/tiles/7_29_44.png +0 -0
- data/examples/tmp/tiles/7_29_45.png +0 -0
- data/examples/tmp/tiles/7_29_46.png +0 -0
- data/examples/tmp/tiles/7_29_47.png +0 -0
- data/examples/tmp/tiles/7_29_48.png +0 -0
- data/examples/tmp/tiles/7_29_49.png +0 -0
- data/examples/tmp/tiles/7_29_50.png +0 -0
- data/examples/tmp/tiles/7_30_43.png +0 -0
- data/examples/tmp/tiles/7_30_44.png +0 -0
- data/examples/tmp/tiles/7_30_45.png +0 -0
- data/examples/tmp/tiles/7_30_46.png +0 -0
- data/examples/tmp/tiles/7_30_47.png +0 -0
- data/examples/tmp/tiles/7_30_48.png +0 -0
- data/examples/tmp/tiles/7_30_49.png +0 -0
- data/examples/tmp/tiles/7_30_50.png +0 -0
- data/examples/tmp/tiles/7_31_43.png +0 -0
- data/examples/tmp/tiles/7_31_44.png +0 -0
- data/examples/tmp/tiles/7_31_45.png +0 -0
- data/examples/tmp/tiles/7_31_46.png +0 -0
- data/examples/tmp/tiles/7_31_47.png +0 -0
- data/examples/tmp/tiles/7_31_48.png +0 -0
- data/examples/tmp/tiles/7_31_49.png +0 -0
- data/examples/tmp/tiles/7_31_50.png +0 -0
- data/examples/tmp/tiles/7_32_43.png +0 -0
- data/examples/tmp/tiles/7_32_44.png +0 -0
- data/examples/tmp/tiles/7_32_45.png +0 -0
- data/examples/tmp/tiles/7_32_46.png +0 -0
- data/examples/tmp/tiles/7_32_47.png +0 -0
- data/examples/tmp/tiles/7_32_48.png +0 -0
- data/examples/tmp/tiles/7_32_49.png +0 -0
- data/examples/tmp/tiles/7_32_50.png +0 -0
- data/examples/tmp/tiles/7_37_71.png +0 -0
- data/examples/tmp/tiles/7_37_72.png +0 -0
- data/examples/tmp/tiles/7_37_73.png +0 -0
- data/examples/tmp/tiles/7_37_74.png +0 -0
- data/examples/tmp/tiles/7_37_75.png +0 -0
- data/examples/tmp/tiles/7_37_76.png +0 -0
- data/examples/tmp/tiles/7_37_77.png +0 -0
- data/examples/tmp/tiles/7_37_78.png +0 -0
- data/examples/tmp/tiles/7_37_79.png +0 -0
- data/examples/tmp/tiles/7_37_80.png +0 -0
- data/examples/tmp/tiles/7_37_81.png +0 -0
- data/examples/tmp/tiles/7_37_82.png +0 -0
- data/examples/tmp/tiles/7_37_83.png +0 -0
- data/examples/tmp/tiles/7_37_84.png +0 -0
- data/examples/tmp/tiles/7_37_85.png +0 -0
- data/examples/tmp/tiles/7_37_86.png +0 -0
- data/examples/tmp/tiles/7_37_87.png +0 -0
- data/examples/tmp/tiles/7_38_71.png +0 -0
- data/examples/tmp/tiles/7_38_72.png +0 -0
- data/examples/tmp/tiles/7_38_73.png +0 -0
- data/examples/tmp/tiles/7_38_74.png +0 -0
- data/examples/tmp/tiles/7_38_75.png +0 -0
- data/examples/tmp/tiles/7_38_76.png +0 -0
- data/examples/tmp/tiles/7_38_77.png +0 -0
- data/examples/tmp/tiles/7_38_78.png +0 -0
- data/examples/tmp/tiles/7_38_79.png +0 -0
- data/examples/tmp/tiles/7_38_80.png +0 -0
- data/examples/tmp/tiles/7_38_81.png +0 -0
- data/examples/tmp/tiles/7_38_82.png +0 -0
- data/examples/tmp/tiles/7_38_83.png +0 -0
- data/examples/tmp/tiles/7_38_84.png +0 -0
- data/examples/tmp/tiles/7_38_85.png +0 -0
- data/examples/tmp/tiles/7_38_86.png +0 -0
- data/examples/tmp/tiles/7_38_87.png +0 -0
- data/examples/tmp/tiles/7_39_71.png +0 -0
- data/examples/tmp/tiles/7_39_72.png +0 -0
- data/examples/tmp/tiles/7_39_73.png +0 -0
- data/examples/tmp/tiles/7_39_74.png +0 -0
- data/examples/tmp/tiles/7_39_75.png +0 -0
- data/examples/tmp/tiles/7_39_76.png +0 -0
- data/examples/tmp/tiles/7_39_77.png +0 -0
- data/examples/tmp/tiles/7_39_78.png +0 -0
- data/examples/tmp/tiles/7_39_79.png +0 -0
- data/examples/tmp/tiles/7_39_80.png +0 -0
- data/examples/tmp/tiles/7_39_81.png +0 -0
- data/examples/tmp/tiles/7_39_82.png +0 -0
- data/examples/tmp/tiles/7_39_83.png +0 -0
- data/examples/tmp/tiles/7_39_84.png +0 -0
- data/examples/tmp/tiles/7_39_85.png +0 -0
- data/examples/tmp/tiles/7_39_86.png +0 -0
- data/examples/tmp/tiles/7_39_87.png +0 -0
- data/examples/tmp/tiles/7_40_71.png +0 -0
- data/examples/tmp/tiles/7_40_72.png +0 -0
- data/examples/tmp/tiles/7_40_73.png +0 -0
- data/examples/tmp/tiles/7_40_74.png +0 -0
- data/examples/tmp/tiles/7_40_75.png +0 -0
- data/examples/tmp/tiles/7_40_76.png +0 -0
- data/examples/tmp/tiles/7_40_77.png +0 -0
- data/examples/tmp/tiles/7_40_78.png +0 -0
- data/examples/tmp/tiles/7_40_79.png +0 -0
- data/examples/tmp/tiles/7_40_80.png +0 -0
- data/examples/tmp/tiles/7_40_81.png +0 -0
- data/examples/tmp/tiles/7_40_82.png +0 -0
- data/examples/tmp/tiles/7_40_83.png +0 -0
- data/examples/tmp/tiles/7_40_84.png +0 -0
- data/examples/tmp/tiles/7_40_85.png +0 -0
- data/examples/tmp/tiles/7_40_86.png +0 -0
- data/examples/tmp/tiles/7_40_87.png +0 -0
- data/examples/tmp/tiles/7_41_71.png +0 -0
- data/examples/tmp/tiles/7_41_72.png +0 -0
- data/examples/tmp/tiles/7_41_73.png +0 -0
- data/examples/tmp/tiles/7_41_74.png +0 -0
- data/examples/tmp/tiles/7_41_75.png +0 -0
- data/examples/tmp/tiles/7_41_76.png +0 -0
- data/examples/tmp/tiles/7_41_77.png +0 -0
- data/examples/tmp/tiles/7_41_78.png +0 -0
- data/examples/tmp/tiles/7_41_79.png +0 -0
- data/examples/tmp/tiles/7_41_80.png +0 -0
- data/examples/tmp/tiles/7_41_81.png +0 -0
- data/examples/tmp/tiles/7_41_82.png +0 -0
- data/examples/tmp/tiles/7_41_83.png +0 -0
- data/examples/tmp/tiles/7_41_84.png +0 -0
- data/examples/tmp/tiles/7_41_85.png +0 -0
- data/examples/tmp/tiles/7_41_86.png +0 -0
- data/examples/tmp/tiles/7_41_87.png +0 -0
- data/examples/tmp/tiles/7_42_71.png +0 -0
- data/examples/tmp/tiles/7_42_72.png +0 -0
- data/examples/tmp/tiles/7_42_73.png +0 -0
- data/examples/tmp/tiles/7_42_74.png +0 -0
- data/examples/tmp/tiles/7_42_75.png +0 -0
- data/examples/tmp/tiles/7_42_76.png +0 -0
- data/examples/tmp/tiles/7_42_77.png +0 -0
- data/examples/tmp/tiles/7_42_78.png +0 -0
- data/examples/tmp/tiles/7_42_79.png +0 -0
- data/examples/tmp/tiles/7_42_80.png +0 -0
- data/examples/tmp/tiles/7_42_81.png +0 -0
- data/examples/tmp/tiles/7_42_82.png +0 -0
- data/examples/tmp/tiles/7_42_83.png +0 -0
- data/examples/tmp/tiles/7_42_84.png +0 -0
- data/examples/tmp/tiles/7_42_85.png +0 -0
- data/examples/tmp/tiles/7_42_86.png +0 -0
- data/examples/tmp/tiles/7_42_87.png +0 -0
- data/examples/tmp/tiles/7_43_71.png +0 -0
- data/examples/tmp/tiles/7_43_72.png +0 -0
- data/examples/tmp/tiles/7_43_73.png +0 -0
- data/examples/tmp/tiles/7_43_74.png +0 -0
- data/examples/tmp/tiles/7_43_75.png +0 -0
- data/examples/tmp/tiles/7_43_76.png +0 -0
- data/examples/tmp/tiles/7_43_77.png +0 -0
- data/examples/tmp/tiles/7_43_78.png +0 -0
- data/examples/tmp/tiles/7_43_79.png +0 -0
- data/examples/tmp/tiles/7_43_80.png +0 -0
- data/examples/tmp/tiles/7_43_81.png +0 -0
- data/examples/tmp/tiles/7_43_82.png +0 -0
- data/examples/tmp/tiles/7_43_83.png +0 -0
- data/examples/tmp/tiles/7_43_84.png +0 -0
- data/examples/tmp/tiles/7_43_85.png +0 -0
- data/examples/tmp/tiles/7_43_86.png +0 -0
- data/examples/tmp/tiles/7_43_87.png +0 -0
- data/examples/tmp/tiles/7_44_71.png +0 -0
- data/examples/tmp/tiles/7_44_72.png +0 -0
- data/examples/tmp/tiles/7_44_73.png +0 -0
- data/examples/tmp/tiles/7_44_74.png +0 -0
- data/examples/tmp/tiles/7_44_75.png +0 -0
- data/examples/tmp/tiles/7_44_76.png +0 -0
- data/examples/tmp/tiles/7_44_77.png +0 -0
- data/examples/tmp/tiles/7_44_78.png +0 -0
- data/examples/tmp/tiles/7_44_79.png +0 -0
- data/examples/tmp/tiles/7_44_80.png +0 -0
- data/examples/tmp/tiles/7_44_81.png +0 -0
- data/examples/tmp/tiles/7_44_82.png +0 -0
- data/examples/tmp/tiles/7_44_83.png +0 -0
- data/examples/tmp/tiles/7_44_84.png +0 -0
- data/examples/tmp/tiles/7_44_85.png +0 -0
- data/examples/tmp/tiles/7_44_86.png +0 -0
- data/examples/tmp/tiles/7_44_87.png +0 -0
- data/examples/tmp/tiles/7_75_64.png +0 -0
- data/examples/tmp/tiles/7_75_65.png +0 -0
- data/examples/tmp/tiles/7_75_66.png +0 -0
- data/examples/tmp/tiles/7_75_67.png +0 -0
- data/examples/tmp/tiles/7_75_68.png +0 -0
- data/examples/tmp/tiles/7_76_64.png +0 -0
- data/examples/tmp/tiles/7_76_65.png +0 -0
- data/examples/tmp/tiles/7_76_66.png +0 -0
- data/examples/tmp/tiles/7_76_67.png +0 -0
- data/examples/tmp/tiles/7_76_68.png +0 -0
- data/examples/tmp/tiles/7_77_64.png +0 -0
- data/examples/tmp/tiles/7_77_65.png +0 -0
- data/examples/tmp/tiles/7_77_66.png +0 -0
- data/examples/tmp/tiles/7_77_67.png +0 -0
- data/examples/tmp/tiles/7_77_68.png +0 -0
- data/lib/gd/gis/basemap.rb +61 -0
- data/lib/gd/gis/layer_points.rb +25 -0
- data/lib/gd/gis/map.rb +108 -0
- data/lib/gd/gis/projection.rb +25 -0
- data/lib/gd/gis.rb +3 -0
- data/lib/libgd_gis.rb +44 -0
- metadata +281 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 5e2c3fe258b7fdb52d1775d50e18095333bf692416c4b4fadf3659febf174604
|
|
4
|
+
data.tar.gz: 3e1ebd506ff4469f231ada7621a5a10a395464d2c373679ee81aacf4e6c1dd35
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: c9bf3495bb2918619807b7b26a3f26b8cb523e839ec0813b7fdb369f9b26ce78add87f0d322e13acff7c6e265093b7852374482afad538bd90010aba13f5b12e
|
|
7
|
+
data.tar.gz: bd5c40732c0126cc2b69c195cd675f4108d0dfc107da5acd8df00834e61d1d575a8bf1ce4b256a635e8fe80f6b07aafd8cc4fe9a45997805079a6361eb38171d
|
data/README.md
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
# libgd-gis
|
|
2
|
+
|
|
3
|
+
**A geospatial raster engine for Ruby**
|
|
4
|
+
|
|
5
|
+
Render real maps, GeoJSON layers, and geospatial data directly in Ruby using a native raster backend powered by **libgd**.
|
|
6
|
+
|
|
7
|
+
This project brings back something Ruby lost over time:
|
|
8
|
+
the ability to generate **maps, tiles, and geospatial visualizations natively**, without external tools like QGIS, ImageMagick, or Mapbox.
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## 🌍 Example
|
|
13
|
+
|
|
14
|
+
This map of Tanzania was generated entirely in Ruby from a GeoJSON dataset of hydroelectric power plants:
|
|
15
|
+
|
|
16
|
+

|
|
17
|
+
|
|
18
|
+
No JavaScript.
|
|
19
|
+
No QGIS.
|
|
20
|
+
No Mapbox.
|
|
21
|
+
Just Ruby.
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## What is this?
|
|
26
|
+
|
|
27
|
+
`libgd-gis` is a **geospatial rendering engine** for Ruby built on top of [`ruby-libgd`](https://github.com/ggerman/ruby-libgd).
|
|
28
|
+
|
|
29
|
+
It allows you to:
|
|
30
|
+
|
|
31
|
+
- Load GeoJSON, CSV, or any dataset with coordinates
|
|
32
|
+
- Fetch real basemap tiles
|
|
33
|
+
- Reproject WGS84 (lat/lon) into Web Mercator
|
|
34
|
+
- Render points, icons, and layers onto a raster map
|
|
35
|
+
- Generate PNG maps or map tiles
|
|
36
|
+
|
|
37
|
+
This is the same type of pipeline used by professional GIS systems — implemented in Ruby.
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## Installation
|
|
42
|
+
|
|
43
|
+
### System dependency
|
|
44
|
+
|
|
45
|
+
`libgd-gis` depends on **libgd**, via `ruby-libgd`.
|
|
46
|
+
|
|
47
|
+
Install libgd first:
|
|
48
|
+
|
|
49
|
+
**Ubuntu / Debian**
|
|
50
|
+
```
|
|
51
|
+
sudo apt install libgd-dev
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
**macOS**
|
|
55
|
+
```
|
|
56
|
+
brew install gd
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
### Ruby gems
|
|
62
|
+
|
|
63
|
+
```
|
|
64
|
+
gem install ruby-libgd
|
|
65
|
+
gem install libgd-gis
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## Quick Example
|
|
71
|
+
|
|
72
|
+
Render hydroelectric plants from a GeoJSON file:
|
|
73
|
+
|
|
74
|
+
```ruby
|
|
75
|
+
require "json"
|
|
76
|
+
require "gd/gis"
|
|
77
|
+
|
|
78
|
+
geo = JSON.parse(File.read("hydro_plants.geojson"))
|
|
79
|
+
plants = geo["features"]
|
|
80
|
+
|
|
81
|
+
lons = plants.map { |f| f["geometry"]["coordinates"][0] }
|
|
82
|
+
lats = plants.map { |f| f["geometry"]["coordinates"][1] }
|
|
83
|
+
|
|
84
|
+
bbox = [lons.min, lats.min, lons.max, lats.max]
|
|
85
|
+
|
|
86
|
+
map = GD::GIS::Map.new(
|
|
87
|
+
bbox: bbox,
|
|
88
|
+
zoom: 7,
|
|
89
|
+
basemap: :carto_light
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
map.add_points(
|
|
93
|
+
plants,
|
|
94
|
+
lon: ->(f) { f["geometry"]["coordinates"][0] },
|
|
95
|
+
lat: ->(f) { f["geometry"]["coordinates"][1] },
|
|
96
|
+
icon: "hydro.png"
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
map.render
|
|
100
|
+
map.save("tanzania_hydro.png")
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
## Features
|
|
106
|
+
|
|
107
|
+
- Real basemap tiles
|
|
108
|
+
- WGS84 → Web Mercator projection
|
|
109
|
+
- GeoJSON point rendering
|
|
110
|
+
- CSV / JSON support
|
|
111
|
+
- Icon-based symbol layers
|
|
112
|
+
- Automatic bounding box fitting
|
|
113
|
+
- Raster output (PNG)
|
|
114
|
+
|
|
115
|
+
---
|
|
116
|
+
|
|
117
|
+
## License
|
|
118
|
+
|
|
119
|
+
MIT
|
|
120
|
+
|
|
121
|
+
---
|
|
122
|
+
|
|
123
|
+
## Author
|
|
124
|
+
|
|
125
|
+
Germán Silva
|
|
126
|
+
https://github.com/ggerman
|
|
127
|
+
https://rubystacknews.com
|
|
Binary file
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
require "net/http"
|
|
2
|
+
require "uri"
|
|
3
|
+
require "fileutils"
|
|
4
|
+
require "gd"
|
|
5
|
+
|
|
6
|
+
ZOOM = 7
|
|
7
|
+
TILE_SIZE = 256
|
|
8
|
+
|
|
9
|
+
# Argentina bounding box (WGS84)
|
|
10
|
+
WEST = -73.6
|
|
11
|
+
EAST = -53.6
|
|
12
|
+
NORTH = -21.8
|
|
13
|
+
SOUTH = -55.1
|
|
14
|
+
|
|
15
|
+
def lon2tile(lon, z)
|
|
16
|
+
((lon + 180.0) / 360.0 * (2 ** z)).floor
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def lat2tile(lat, z)
|
|
20
|
+
rad = lat * Math::PI / 180
|
|
21
|
+
((1 - Math.log(Math.tan(rad) + 1 / Math.cos(rad)) / Math::PI) / 2 * (2 ** z)).floor
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Compute tile range for Argentina
|
|
25
|
+
x_min = lon2tile(WEST, ZOOM)
|
|
26
|
+
x_max = lon2tile(EAST, ZOOM)
|
|
27
|
+
y_min = lat2tile(NORTH, ZOOM)
|
|
28
|
+
y_max = lat2tile(SOUTH, ZOOM)
|
|
29
|
+
|
|
30
|
+
puts "Argentina tiles at z=#{ZOOM}:"
|
|
31
|
+
puts "x: #{x_min} .. #{x_max}"
|
|
32
|
+
puts "y: #{y_min} .. #{y_max}"
|
|
33
|
+
|
|
34
|
+
COLS = x_max - x_min + 1
|
|
35
|
+
ROWS = y_max - y_min + 1
|
|
36
|
+
|
|
37
|
+
WIDTH = COLS * TILE_SIZE
|
|
38
|
+
HEIGHT = ROWS * TILE_SIZE
|
|
39
|
+
|
|
40
|
+
FileUtils.mkdir_p("tmp/tiles")
|
|
41
|
+
FileUtils.mkdir_p("output")
|
|
42
|
+
|
|
43
|
+
def fetch_tile(z, x, y)
|
|
44
|
+
path = "tmp/tiles/#{z}_#{x}_#{y}.png"
|
|
45
|
+
return path if File.exist?(path)
|
|
46
|
+
|
|
47
|
+
url = URI("https://basemaps.cartocdn.com/light_all/#{z}/#{x}/#{y}.png")
|
|
48
|
+
puts "Downloading #{url}"
|
|
49
|
+
|
|
50
|
+
Net::HTTP.start(url.host, url.port, use_ssl: true) do |http|
|
|
51
|
+
req = Net::HTTP::Get.new(url)
|
|
52
|
+
req["User-Agent"] = "ruby-libgd-gis"
|
|
53
|
+
res = http.request(req)
|
|
54
|
+
raise "Tile failed #{z}/#{x}/#{y}" unless res.code == "200"
|
|
55
|
+
File.binwrite(path, res.body)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
path
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
puts "Downloading tiles..."
|
|
62
|
+
|
|
63
|
+
(x_min..x_max).each do |x|
|
|
64
|
+
(y_min..y_max).each do |y|
|
|
65
|
+
fetch_tile(ZOOM, x, y)
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
puts "Composing map..."
|
|
70
|
+
|
|
71
|
+
map = GD::Image.new(WIDTH, HEIGHT)
|
|
72
|
+
|
|
73
|
+
(x_min..x_max).each_with_index do |x, cx|
|
|
74
|
+
(y_min..y_max).each_with_index do |y, cy|
|
|
75
|
+
tile = GD::Image.open("tmp/tiles/#{ZOOM}_#{x}_#{y}.png")
|
|
76
|
+
map.copy(tile, cx * TILE_SIZE, cy * TILE_SIZE, 0, 0, TILE_SIZE, TILE_SIZE)
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
map.save("output/argentina.png")
|
|
81
|
+
|
|
82
|
+
puts "Saved output/argentina.png"
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
require "json"
|
|
2
|
+
require "gd"
|
|
3
|
+
|
|
4
|
+
# --------------------------------
|
|
5
|
+
# Input / output
|
|
6
|
+
# --------------------------------
|
|
7
|
+
BASE_MAP = "output/argentina.png"
|
|
8
|
+
DATA = "localidades.json"
|
|
9
|
+
OUTPUT = "output/argentina_localidades.png"
|
|
10
|
+
|
|
11
|
+
# --------------------------------
|
|
12
|
+
# Bounding box de Argentina (WGS84)
|
|
13
|
+
# --------------------------------
|
|
14
|
+
ARG_MIN_LON = -73.6
|
|
15
|
+
ARG_MAX_LON = -53.6
|
|
16
|
+
ARG_MIN_LAT = -55.1
|
|
17
|
+
ARG_MAX_LAT = -21.8
|
|
18
|
+
|
|
19
|
+
R = 6378137.0
|
|
20
|
+
|
|
21
|
+
def mercator_x(lon)
|
|
22
|
+
lon * Math::PI / 180.0 * R
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def mercator_y(lat)
|
|
26
|
+
Math.log(Math.tan(Math::PI/4 + lat * Math::PI / 360.0)) * R
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# --------------------------------
|
|
30
|
+
# Cargar mapa base
|
|
31
|
+
# --------------------------------
|
|
32
|
+
img = GD::Image.open(BASE_MAP)
|
|
33
|
+
|
|
34
|
+
width = img.width
|
|
35
|
+
height = img.height
|
|
36
|
+
|
|
37
|
+
flag = GD::Image.open("flag.png")
|
|
38
|
+
flag_w = flag.width
|
|
39
|
+
flag_h = flag.height
|
|
40
|
+
|
|
41
|
+
# --------------------------------
|
|
42
|
+
# Bounding box proyectado
|
|
43
|
+
# --------------------------------
|
|
44
|
+
min_x = mercator_x(ARG_MIN_LON)
|
|
45
|
+
max_x = mercator_x(ARG_MAX_LON)
|
|
46
|
+
min_y = mercator_y(ARG_MIN_LAT)
|
|
47
|
+
max_y = mercator_y(ARG_MAX_LAT)
|
|
48
|
+
|
|
49
|
+
def lonlat_to_pixel(lon, lat, min_x, max_x, min_y, max_y, width, height)
|
|
50
|
+
x = mercator_x(lon)
|
|
51
|
+
y = mercator_y(lat)
|
|
52
|
+
|
|
53
|
+
px = (x - min_x) / (max_x - min_x) * width
|
|
54
|
+
py = height - (y - min_y) / (max_y - min_y) * height
|
|
55
|
+
|
|
56
|
+
[px.to_i, py.to_i]
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# --------------------------------
|
|
60
|
+
# Cargar localidades
|
|
61
|
+
# --------------------------------
|
|
62
|
+
data = JSON.parse(File.read(DATA))
|
|
63
|
+
|
|
64
|
+
red = GD::Color.rgb(220, 40, 40)
|
|
65
|
+
|
|
66
|
+
puts "Plotting #{data["localidades"].size} localidades..."
|
|
67
|
+
|
|
68
|
+
data["localidades"].each do |loc|
|
|
69
|
+
lon = loc["centroide"]["lon"]
|
|
70
|
+
lat = loc["centroide"]["lat"]
|
|
71
|
+
|
|
72
|
+
x, y = lonlat_to_pixel(lon, lat, min_x, max_x, min_y, max_y, width, height)
|
|
73
|
+
|
|
74
|
+
# dibujar un punto
|
|
75
|
+
img.copy(
|
|
76
|
+
flag,
|
|
77
|
+
x - flag_w / 2,
|
|
78
|
+
y - flag_h / 2,
|
|
79
|
+
0, 0,
|
|
80
|
+
flag_w,
|
|
81
|
+
flag_h
|
|
82
|
+
)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# --------------------------------
|
|
86
|
+
# Guardar resultado
|
|
87
|
+
# --------------------------------
|
|
88
|
+
img.save(OUTPUT)
|
|
89
|
+
puts "Saved #{OUTPUT}"
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
require "csv"
|
|
2
|
+
require "gd"
|
|
3
|
+
require "pry"
|
|
4
|
+
|
|
5
|
+
ZOOM = 7
|
|
6
|
+
TILE_SIZE = 256
|
|
7
|
+
|
|
8
|
+
# Bounding box Argentina (igual que en argentina_tiles.rb)
|
|
9
|
+
WEST = -73.6
|
|
10
|
+
EAST = -53.6
|
|
11
|
+
NORTH = -21.8
|
|
12
|
+
SOUTH = -55.1
|
|
13
|
+
|
|
14
|
+
def lon2tile(lon, z)
|
|
15
|
+
((lon + 180.0) / 360.0 * (2 ** z)).floor
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def lat2tile(lat, z)
|
|
19
|
+
rad = lat * Math::PI / 180
|
|
20
|
+
((1 - Math.log(Math.tan(rad) + 1 / Math.cos(rad)) / Math::PI) / 2 * (2 ** z)).floor
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def lon2px(lon, z)
|
|
24
|
+
((lon + 180.0) / 360.0 * (256 * 2**z))
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def lat2px(lat, z)
|
|
28
|
+
rad = lat * Math::PI / 180
|
|
29
|
+
((1 - Math.log(Math.tan(rad) + 1 / Math.cos(rad)) / Math::PI) / 2 * (256 * 2**z))
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Same tile bbox used to build argentina.png
|
|
33
|
+
x_min = lon2tile(WEST, ZOOM)
|
|
34
|
+
y_min = lat2tile(NORTH, ZOOM)
|
|
35
|
+
|
|
36
|
+
offset_x = x_min * TILE_SIZE
|
|
37
|
+
offset_y = y_min * TILE_SIZE
|
|
38
|
+
|
|
39
|
+
# Load map
|
|
40
|
+
img = GD::Image.open("output/argentina.png")
|
|
41
|
+
|
|
42
|
+
# Load icon
|
|
43
|
+
icon = GD::Image.open("museos.png")
|
|
44
|
+
iw = icon.width
|
|
45
|
+
ih = icon.height
|
|
46
|
+
|
|
47
|
+
puts "Plotting museums..."
|
|
48
|
+
|
|
49
|
+
CSV.foreach("museos_datosabiertos.csv", headers: true, encoding: "bom|utf-8") do |row|
|
|
50
|
+
next if row["Latitud"].nil? || row["Longitud"].nil?
|
|
51
|
+
|
|
52
|
+
lat = row["Latitud"].to_f
|
|
53
|
+
lon = row["Longitud"].to_f
|
|
54
|
+
|
|
55
|
+
# global pixel in Web Mercator
|
|
56
|
+
px = lon2px(lon, ZOOM)
|
|
57
|
+
py = lat2px(lat, ZOOM)
|
|
58
|
+
|
|
59
|
+
# local pixel inside argentina.png
|
|
60
|
+
x = px - offset_x
|
|
61
|
+
y = py - offset_y
|
|
62
|
+
|
|
63
|
+
# skip if outside raster
|
|
64
|
+
next if x < 0 || y < 0 || x >= img.width || y >= img.height
|
|
65
|
+
|
|
66
|
+
img.copy(icon, x.to_i - iw/2, y.to_i - ih/2, 0, 0, iw, ih)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
img.save("output/argentina_museos.png")
|
|
70
|
+
puts "Saved output/argentina_museos.png"
|
data/examples/flag.png
ADDED
|
Binary file
|
data/examples/hydro.png
ADDED
|
Binary file
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"type": "FeatureCollection",
|
|
3
|
+
"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:OGC:1.3:CRS84" } },
|
|
4
|
+
"features": [
|
|
5
|
+
{ "type": "Feature", "properties": { "id": 0, "status": "Existing", "source": "World Bank", "country": "Tanzania", "name": "Kagera", "type": "Hydro", "capacit_MW": 0, "operator": null, "year": 0, "difference": "T" }, "geometry": { "type": "Point", "coordinates": [ 31.0933382, -1.196415 ] } },
|
|
6
|
+
{ "type": "Feature", "properties": { "id": 7, "status": "Existing", "source": "World Bank", "country": "Tanzania", "name": "Nyumba ya Mungu", "type": "Hydro", "capacit_MW": 0, "operator": null, "year": 0, "difference": null }, "geometry": { "type": "Point", "coordinates": [ 37.4403441, -3.8195803 ] } },
|
|
7
|
+
{ "type": "Feature", "properties": { "id": 8, "status": "Existing", "source": "World Bank", "country": "Tanzania", "name": "Chutes Pangani I", "type": "Hydro", "capacit_MW": 0, "operator": null, "year": 0, "difference": "T" }, "geometry": { "type": "Point", "coordinates": [ 38.759691784504, -5.343275566027487 ] } },
|
|
8
|
+
{ "type": "Feature", "properties": { "id": 9, "status": "Existing", "source": "World Bank", "country": "Tanzania", "name": "Chutes Pangani II", "type": "Hydro", "capacit_MW": 0, "operator": null, "year": 0, "difference": null }, "geometry": { "type": "Point", "coordinates": [ 38.481225781243062, -5.269153170191239 ] } },
|
|
9
|
+
{ "type": "Feature", "properties": { "id": 10, "status": "Existing", "source": "World Bank", "country": "Tanzania", "name": "New Pangani", "type": "Hydro", "capacit_MW": 0, "operator": null, "year": 0, "difference": null }, "geometry": { "type": "Point", "coordinates": [ 38.591509346890959, -5.378960936287418 ] } },
|
|
10
|
+
{ "type": "Feature", "properties": { "id": 11, "status": "Existing", "source": "World Bank", "country": "Tanzania", "name": "Hale Tanesco", "type": "Hydro", "capacit_MW": 0, "operator": null, "year": 0, "difference": "T" }, "geometry": { "type": "Point", "coordinates": [ 38.635622773150118, -5.225224606056146 ] } },
|
|
11
|
+
{ "type": "Feature", "properties": { "id": 13, "status": "Existing", "source": "World Bank", "country": "Tanzania", "name": "Mufindi", "type": "Hydro", "capacit_MW": 0, "operator": null, "year": 0, "difference": "T" }, "geometry": { "type": "Point", "coordinates": [ 35.5402297, -7.9807668 ] } },
|
|
12
|
+
{ "type": "Feature", "properties": { "id": 14, "status": "Existing", "source": "World Bank", "country": "Tanzania", "name": "Mtera", "type": "Hydro", "capacit_MW": 0, "operator": null, "year": 0, "difference": null }, "geometry": { "type": "Point", "coordinates": [ 35.891170578822909, -7.096171197430225 ] } },
|
|
13
|
+
{ "type": "Feature", "properties": { "id": 15, "status": "Existing", "source": "World Bank", "country": "Tanzania", "name": "Kidata", "type": "Hydro", "capacit_MW": 0, "operator": null, "year": 0, "difference": null }, "geometry": { "type": "Point", "coordinates": [ 36.970001220703125, -7.679999828338623 ] } },
|
|
14
|
+
{ "type": "Feature", "properties": { "id": 16, "status": "Existing", "source": "World Bank", "country": "Tanzania", "name": "Bas Kihansi", "type": "Hydro", "capacit_MW": 0, "operator": null, "year": 0, "difference": "T" }, "geometry": { "type": "Point", "coordinates": [ 35.8792647, -8.6122779 ] } },
|
|
15
|
+
{ "type": "Feature", "properties": { "id": 19, "status": "Existing", "source": "World Bank", "country": "Tanzania", "name": "Ruhudji", "type": "Hydro", "capacit_MW": 0, "operator": null, "year": 0, "difference": null }, "geometry": { "type": "Point", "coordinates": [ 34.779586311911132, -9.462585584742733 ] } },
|
|
16
|
+
{ "type": "Feature", "properties": { "id": 0, "status": "Proposed", "source": "Tanesco", "country": "Tanzania", "name": "Masigira", "type": "Hydro", "capacit_MW": 0, "operator": null, "year": 2015, "difference": null }, "geometry": { "type": "Point", "coordinates": [ 35.057617146214611, -9.858152893694784 ] } },
|
|
17
|
+
{ "type": "Feature", "properties": { "id": 0, "status": "Proposed", "source": "Tanesco", "country": "Tanzania", "name": "Ruhudji", "type": "Hydro", "capacit_MW": 0, "operator": null, "year": 2015, "difference": null }, "geometry": { "type": "Point", "coordinates": [ 35.432706580089054, -9.2930181466573 ] } },
|
|
18
|
+
{ "type": "Feature", "properties": { "id": 0, "status": "Proposed", "source": "Tanesco", "country": "Tanzania", "name": "Nakatuta", "type": "Hydro", "capacit_MW": 0, "operator": null, "year": 2015, "difference": null }, "geometry": { "type": "Point", "coordinates": [ 35.472716119702326, -11.15346173867451 ] } },
|
|
19
|
+
{ "type": "Feature", "properties": { "id": 0, "status": "Existing", "source": "Tanesco", "country": "Tanzania", "name": "Sunda Falls(MHP)", "type": "Hydro", "capacit_MW": 0, "operator": null, "year": 2015, "difference": null }, "geometry": { "type": "Point", "coordinates": [ 36.89805596842519, -11.413523746160788 ] } },
|
|
20
|
+
{ "type": "Feature", "properties": { "id": 0, "status": "Existing", "source": "Tanesco", "country": "Tanzania", "name": "Kihansi", "type": "Hydro", "capacit_MW": 0, "operator": null, "year": 2015, "difference": null }, "geometry": { "type": "Point", "coordinates": [ 35.860183447113322, -8.140235053135704 ] } },
|
|
21
|
+
{ "type": "Feature", "properties": { "id": 0, "status": "Existing", "source": "Tanesco", "country": "Tanzania", "name": "Tosamaganga", "type": "Hydro", "capacit_MW": 0, "operator": null, "year": 2015, "difference": null }, "geometry": { "type": "Point", "coordinates": [ 35.385145143948549, -7.423783841805231 ] } },
|
|
22
|
+
{ "type": "Feature", "properties": { "id": 0, "status": "Proposed", "source": "Tanesco", "country": "Tanzania", "name": "Mpanga", "type": "Hydro", "capacit_MW": 0, "operator": null, "year": 2015, "difference": null }, "geometry": { "type": "Point", "coordinates": [ 35.752782896995242, -8.727883399619813 ] } },
|
|
23
|
+
{ "type": "Feature", "properties": { "id": 0, "status": "Proposed", "source": "Tanesco", "country": "Tanzania", "name": "Rumakali", "type": "Hydro", "capacit_MW": 0, "operator": null, "year": 2015, "difference": null }, "geometry": { "type": "Point", "coordinates": [ 33.832324995558118, -8.942934675041157 ] } }
|
|
24
|
+
]
|
|
25
|
+
}
|