libgd-gis continues to grow — now with styles and more

January 12, 2026

Real-world cartography in pure Ruby

RubyStackNews — January 2026


From geometry to cities

Until recently, libgd-gis could render raw GeoJSON.

Now it renders cities.

Over the last development cycle, libgd-gis evolved from a low-level geometry renderer into a style-aware, layered GIS engine capable of producing publication-quality maps — directly from Ruby.

With the addition of:

  • A style engine
  • Layered GeoJSON
  • The map.image drawing API
  • A labeling system

Ruby can now generate streets, rivers, railways, parks, points of interest, and full city layouts without Mapbox, without QGIS, and without JavaScript.

Everything shown in New York, Paris, Tokyo, and Paraná was rendered entirely in Ruby.


Styles — turning data into cartography

Article content

Raw GIS data is unreadable without visual hierarchy. A million lines mean nothing until they are styled.

libgd-gis now supports cartographic styles via YAML:

roads:
motorway:
stroke: [255,255,255]
stroke_width: 10
fill: [60,60,60]
fill_width: 6
primary:
stroke: [200,200,200]
stroke_width: 7
fill: [80,80,80]
fill_width: 4
water:
stroke: [0,120,255]
stroke_width: 6
fill: [0,90,200]

Styles control:

  • Color palettes
  • Stroke and fill layers
  • Line hierarchy
  • Visual weight

This is why Tokyo’s rivers read clearly, why Paris parks stand out, and why road networks acquire depth instead of noise.


GeoJSON as real layers

Article content

Instead of dumping everything into a single stream, libgd-gis now treats GeoJSON as semantic layers:

map.add_geojson("streets.geojson")
map.add_geojson("railways.geojson")
map.add_geojson("rivers.geojson")
map.add_geojson("parks.geojson")

Features are classified into:

  • :roads
  • :rail
  • :water
  • :landuse
  • :points

This allows correct draw order, per-layer styling, and precise label placement. Rivers flow under bridges, rails sit above streets, and parks fill behind labels.


map.image — full graphic control

map.image exposes the raw GD canvas behind every map.

This allows you to draw UI elements, banners, and overlays directly on top of the GIS output:

map.image.filled_rectangle(x, y, x + w, y + h, [0,0,0,160])
map.image.text(x + 20, y + 40, "PARIS",
font: "Inter-Bold.ttf",
size: 48,
color: [255,255,255]
)

This is how city titles, dark headers, watermarks, and branding are composited into the maps.

GIS and graphic design now live in the same Ruby pipeline.


Labels for roads, rivers, and places

Article content

The new labeling engine understands geometry type:

GeometryLabel behaviorLineString (roads, rivers)Text follows curvesPolygon (parks, areas)Centered labelsPointsIcon + name

map.add_geojson("rivers.geojson", labels: true)
map.add_points(museums, label: ->(r){ r["name"] })
map.add_geojson("wards.geojson", label_property: "name")

This produces readable, non-overlapping labels for:

  • Rivers
  • Parks
  • Neighborhoods
  • Points of interest

Even in dense cities like Tokyo.


Real cities, rendered in Ruby

Article content
Manhattan, Crentral Park, Streets and Bridges

Every image in this article was generated from:

  • OpenStreetMap GeoJSON
  • Ruby
  • libgd-gis
  • ruby-libgd

No tiles. No screenshots. No JavaScript.

New York, Paris, Tokyo, and Paraná are computed as real raster maps.


Why this matters

Ruby now has a true raster GIS engine.

That enables:

  • Scientific and journalistic cartography
  • Automated map generation
  • High-resolution exports
  • GIS in CI pipelines
  • Data-driven visualization

All from pure Ruby.


What’s next

Development is now moving toward:

  • More advanced text placement
  • Icon systems
  • Additional OSM classifiers
  • Light and dark themes
  • Expanding documentation

libgd-gis is no longer an experiment — it is becoming a full cartographic stack for Ruby.

Leave a comment