libgd-gis 0.2.4 → 0.2.5

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 31c9d884a7601b749b9ef7c51852ab27e440284a47535119ecb1715f8257e510
4
- data.tar.gz: 152d65acaabc7626f47c3ec2e77d71fa45479694395076727a71d598ebe84025
3
+ metadata.gz: 15adf406fc3424953ad3ac2fcf5d05bfab4d02268619793fabc2bc5ed5ec67f7
4
+ data.tar.gz: 3a073e0eab8c647fb9a142e5675e1bb138c58a68cb0f31fb03e09f063f848f7d
5
5
  SHA512:
6
- metadata.gz: 74debf00d8816b99beac0d59775b27b525c0b0f0db9cc41c39f2f48396e1cefcffd90a0ee658d9a1a08a89e6b661fa0547a21171bd638575bcd29c5e603ad967
7
- data.tar.gz: b8a4a1268a2da42c554c4b0fb24705fc3f1fcdab8915992aad113d6d611e3d4863ebe1eddf9c48050474c736ba54a23ea9d222f20a857ba80f65bf6faaadce9e
6
+ metadata.gz: 17a1eebdd7c9a4c1db7ffdff96965b61e5f0ac37548ea9955ed19dbc0812ac723f5be2a7ab203f26135aefeef2d39f44eff60ba0265bb868a379d5ab86a541ad
7
+ data.tar.gz: 0dd61743522c231ebf7df34c6c6a4da6dffc3c42fb539d5020a17f3a70c1a3ab5cc3fb78d6529f620fc5323a39cf2c02cd0097a3abc9e2f846a1a8f799727b2b
data/README.md CHANGED
@@ -29,10 +29,15 @@
29
29
  </p>
30
30
 
31
31
  ![CI](https://github.com/ggerman/libgd-gis/actions/workflows/ci.yml/badge.svg)
32
+ [![Codacy Badge](https://api.codacy.com/project/badge/Grade/6bc3e7d6118d47e6959b16690b815909)](https://www.codacy.com/app/libgd-gis/libgd-gis?utm_source=github.com&amp;utm_medium=referral&amp;utm_content=libgd-gis/libgd-gis&amp;utm_campaign=Badge_Grade)
33
+ [![Test Coverage](https://coveralls.io/repos/githublibgd-gis/libgd-gis/badge.svg?branch=master)](https://coveralls.io/github/libgd-gis/libgd-gis?branch=master)
34
+ [![Gem Version](https://img.shields.io/gem/v/libgd-gis.svg)](https://rubygems.org/gems/libgd-gis)
35
+
32
36
 
33
37
  | Examples | Examples | Examples |
34
38
  | :----: | :----: | :--: |
35
- | <img src="examples/nyc/nyc.png" height="250"> | <img src="examples/tokyo/tokyo.png" height="250"> | <img src="examples/parana/parana.png" height="250"> |
39
+ | <img src="docs/examples/parana.png" height="250"> | <img src="docs/examples/nyc.png" height="250"> | <img src="docs/examples/paris.png" height="250"> |
40
+ | <img src="examples/nyc/nyc.png" height="250"> | <img src="docs/examples/tokyo_solarized.png" height="250"> | <img src="examples/parana/parana.png" height="250"> |
36
41
  | <img src="docs/examples/america.png" height="250"> | <img src="docs/examples/argentina_museum.png" height="250"> | <img src="docs/examples/museos_parana.png" height="250"> |
37
42
  | <img src="docs/examples/asia.png" height="250"> | <img src="docs/examples/europe.png" height="250"> | <img src="docs/examples/icecream_parana.png" height="250"> |
38
43
  | <img src="docs/examples/argentina_cities.png" height="250"> | <img src="docs/examples/tanzania_hydro.png" height="250"> | <img src="docs/examples/parana_polygon.png" height="250"> |
@@ -40,6 +45,12 @@
40
45
 
41
46
  ---
42
47
 
48
+ > **libgd-gis is evolving very fast**, so some examples may temporarily stop working.
49
+ > Please report issues or ask for help — feedback is very welcome.
50
+ > https://github.com/ggerman/libgd-gis/issues or ggerman@gmail.com
51
+
52
+ --
53
+
43
54
  ## A geospatial raster engine for Ruby.
44
55
 
45
56
  libgd-gis allows Ruby to render real maps, GeoJSON layers, vector features, and geospatial tiles using a native raster backend powered by **libgd**.
@@ -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
@@ -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
File without changes
@@ -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
- features = data["features"]
10
- features.map do |f|
11
- Feature.new(f["geometry"], f["properties"])
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/map.rb CHANGED
@@ -47,18 +47,27 @@ module GD
47
47
  features = LayerGeoJSON.load(path)
48
48
 
49
49
  features.each do |feature|
50
- if Classifier.water?(feature)
51
- kind = Classifier.water_kind(feature)
50
+ case feature.layer
51
+ when :water
52
+ # optional: detect river vs canal from properties
53
+ kind =
54
+ case (feature.properties["objeto"] || feature.properties["waterway"]).to_s.downcase
55
+ when /river|río/ then :river
56
+ when /stream|arroyo/ then :stream
57
+ else :minor
58
+ end
59
+
52
60
  @layers[:water] << [kind, feature]
53
61
 
54
- elsif Classifier.park?(feature)
55
- @layers[:park] << feature
62
+ when :roads
63
+ # map to style categories if you want later
64
+ @layers[:street] << feature
56
65
 
57
- elsif Classifier.rail?(feature)
58
- @layers[:rail] << feature
66
+ when :parks
67
+ @layers[:park] << feature
59
68
 
60
- elsif type = Classifier.road(feature)
61
- @layers[type] << feature
69
+ else
70
+ # ignore unclassified for now
62
71
  end
63
72
  end
64
73
  end
@@ -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,23 @@
1
+ water:
2
+ ign:
3
+ objeto:
4
+ - canal
5
+ - río
6
+ - arroyo
7
+ - embalse
8
+ - laguna
9
+ - dique
10
+ - represa
11
+ gna:
12
+ - canal
13
+ - río
14
+ - arroyo
15
+ - embalse
16
+ - laguna
17
+
18
+ natural_earth:
19
+ featurecla:
20
+ - river
21
+ - lake
22
+ - reservoir
23
+ - riverbank
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
4
+ version: 0.2.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Germán Alberto Giménez Silva
@@ -41,14 +41,21 @@ 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/dump.sh
44
+ - lib/gd/gis/crs_normalizer.rb
45
45
  - lib/gd/gis/feature.rb
46
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
47
52
  - lib/gd/gis/layer_geojson.rb
48
53
  - lib/gd/gis/layer_lines.rb
49
54
  - lib/gd/gis/layer_points.rb
50
55
  - lib/gd/gis/layer_polygons.rb
51
56
  - lib/gd/gis/map.rb
57
+ - lib/gd/gis/ontology.rb
58
+ - lib/gd/gis/ontology.yml
52
59
  - lib/gd/gis/projection.rb
53
60
  - lib/gd/gis/style.rb
54
61
  - lib/gd/gis/style/dark.rb
data/lib/gd/gis/dump.sh DELETED
@@ -1,19 +0,0 @@
1
- #!/usr/bin/env bash
2
-
3
- set -e
4
-
5
- OUT="all_ruby_sources.txt"
6
- > "$OUT"
7
-
8
- echo "### Ruby sources dump ($(date))" >> "$OUT"
9
- echo >> "$OUT"
10
-
11
- find . -type f -name "*.rb" | sort | while read -r file; do
12
- echo "===== FILE: $file =====" >> "$OUT"
13
- echo >> "$OUT"
14
- cat "$file" >> "$OUT"
15
- echo >> "$OUT"
16
- echo >> "$OUT"
17
- done
18
-
19
- echo "Wrote $OUT"