Pure Ruby Maps: libgd-gis + Rails

April 8, 2026

Static GIS maps, pure Ruby, GeoJSON support, and zero JavaScript bloat.

Over 15 years ago, I discovered the GD library at a Linux conference. I was fascinated by the ability to generate graphics dynamically in C and PHP with just code. It seemed magical.

For two decades, I kept wondering: Why doesn’t Ruby have solid GD support?

Last year, I built the answer. Today, I’m sharing it: MapView – a Rails gem that renders beautiful static maps without a single line of JavaScript.


The Problem We’re Solving

If you’ve integrated maps into a Rails app, you know the pain:

  • JavaScript overhead – Leaflet, Mapbox, Google Maps add kilobytes to your bundle
  • Complex setup – Configuration, API keys, licensing
  • Expensive – Production usage gets pricey
  • Overkill for static maps – A 200KB library just to show locations?

For delivery apps, real estate listings, store locators, and GIS reports, you often don’t need interactive maps. You need fast, beautiful static images.

That’s where MapView comes in.


The Solution: Pure Ruby Rendering

MapView generates static PNG maps directly from your Rails views using:

  • libgd-gis – GIS rendering engine (built on ruby-libgd)
  • GeoJSON – Standard geographic data format
  • Ruby – Nothing else

No JavaScript. No frontend complexity. Just Rails.

<!-- Single point -->
<%= map_view_point(-58.3816, -34.6037, label: "Buenos Aires", zoom: 12) %>
<!-- Multiple locations from GeoJSON file -->
<%= map_view("public/data/cities.geojson",
bbox: :argentina,
zoom: 6,
width: 800,
height: 600) %>
<!-- Custom styling -->
<%= map_view("public/data/zones.geojson",
style: "dark",
basemap: :carto_dark,
width: 1000,
height: 700) %>

That’s it. Images are cached as PNG files and served instantly.

Article content

How It Works

MapView follows a simple pipeline:

View Helper (map_view or map_view_point)
Detect Input Type (GeoJSON string or file path)
Generate Cache Key (from path, bbox, zoom, style)
Check public/map_cache/
If Found & Not Expired: Return <img> tag
If Not:
Load GeoJSON data
Create GD::GIS::Map with bbox + zoom + basemap
Add GeoJSON features to map
Load YAML style config
Render map with libgd-gis
Save PNG to public/map_cache/
Return <img> tag

The cache key ensures that identical maps are rendered only once. Change the style? New cache entry. Same data? Same image served instantly.


Getting Started

Installation

# Add to Gemfile
gem 'map_view'
# Install
bundle install
# Generate configuration
rails generate map_view:install

This creates:

  • config/initializers/map_view.rb – Configuration
  • config/map_view_styles.yml – Built-in styles
  • public/map_cache/ – Cache directory
Article content

Create Sample Data

Create a GeoJSON file with your locations:

// public/data/cities.geojson
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [-122.4194, 37.7749]
},
"properties": {
"name": "San Francisco",
"state": "CA"
}
},
{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [-118.2437, 34.0522]
},
"properties": {
"name": "Los Angeles",
"state": "CA"
}
}
]
}

Render in View

<h1>US Cities</h1>
<div class="map-container">
<%= map_view("public/data/cities.geojson",
bbox: :united_states,
zoom: 6,
width: 800,
height: 600,
basemap: :carto_light,
style: :default) %>
</div>

That’s it. MapView automatically:

  1. Detects the GeoJSON file
  2. Calculates optimal zoom level (if not specified)
  3. Renders the map with libgd-gis
  4. Caches the PNG
  5. Returns an <img> tag

No JavaScript. No PostGIS required (though it works great with it).


API: The Two Main Helpers

MapView provides two simple helpers:

map_view(geojson_or_path, options = {})

Render a map from GeoJSON data or file.

<!-- From GeoJSON file -->
<%= map_view("public/data/zones.geojson",
bbox: :argentina,
width: 800) %>
<!-- From GeoJSON string (inline) -->
<% geojson = '{"type":"FeatureCollection","features":[...]}' %>
<%= map_view(geojson, zoom: 10) %>

Options:

  • bbox – Bounding box (:argentina, :world, or [min_lng, min_lat, max_lng, max_lat])
  • zoom – Zoom level (1-16, auto-calculated if not specified)
  • basemap – Provider (:carto_light, :osm, :esri_satellite, etc.)
  • width – Image width in pixels (default: 800)
  • height – Image height in pixels (default: 600)
  • style – Style name from YAML config (default: “default”)
  • alt – Alt text for <img> tag
  • force_render – Bypass cache and re-render
Article content

map_view_point(lon, lat, options = {})

Render a single point.

<!-- Simple point -->
<%= map_view_point(-122.4194, 37.7749, label: "San Francisco") %>
<!-- With custom options -->
<%= map_view_point(
-122.4194, 37.7749,
label: "Headquarters",
zoom: 14,
width: 600,
height: 400,
style: :custom) %>

Internally, this creates a FeatureCollection with a single Point and calls map_view().


Real-World Use Cases

Delivery & Logistics

Store multiple delivery points in GeoJSON:

// public/data/deliveries.geojson
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [-122.4194, 37.7749]
},
"properties": {
"address": "123 Main St, SF",
"status": "delivered"
}
}
]
}

Render in order confirmation email:

<%= map_view("public/data/deliveries.geojson",
width: 600,
height: 400) %>

PNG cached. Same data = instant load.

Real Estate Listings

Show properties in an area:

<!-- All listings in neighborhood -->
<%= map_view("public/data/listings.geojson",
bbox: :los_angeles,
style: :real_estate,
width: 800,
height: 600) %>
Article content

Store Locator

Simple store map:

<%= map_view("public/data/stores.geojson",
bbox: :united_states,
zoom: 6,
width: 1000,
height: 700) %>

Marketing Materials

Generate static maps for brochures, reports, emails:

<!-- High-res map for printing -->
<%= map_view("public/data/coverage.geojson",
width: 1200,
height: 900,
force_render: true) %>

Styling Maps

MapView includes built-in styles and YAML-based customization:

# config/map_view_styles.yml
my_delivery_style:
marker_color: [255, 100, 50] # Orange
marker_size: 10
line_color: [0, 122, 255] # Blue
line_width: 3
background_color: [245, 245, 245] # Light gray

Use it:

<%= map_view("public/data/routes.geojson", style: "my_delivery_style") %>

Available basemaps:

  • :osm – OpenStreetMap
  • :carto_light – CARTO Light (default)
  • :carto_dark – CARTO Dark
  • :esri_satellite – ESRI Satellite
  • :stamen_toner – Stamen Toner
  • :stamen_terrain – Stamen Terrain
Article content

Performance: MapView vs Leaflet

Article content

MapView trades interactivity for speed, simplicity, and zero JavaScript.

If your users need to zoom, pan, and click—use Leaflet. If you just need to show locations—use MapView.


The Journey: From GD to MapView

This journey started in 2004. For 20 years, I kept asking: Why doesn’t Ruby have great GD support?

The answer came in pieces:

  1. 2026 (January) – Released ruby-libgd v0.1.0
  2. 2026 (January) – Released libgd-gis v0.1.0
  3. 2026 (April) – Released map_view v0.1.0

Each piece builds on the last. MapView is the practical Rails layer on top of years of work on ruby-libgd and libgd-gis.


Licensing

MapView is available under two licenses:

Open Source (MIT)

Free for personal projects, open-source apps, and evaluation.

gem 'map_view'

Commercial License

For production deployments, commercial applications, and priority support.

Contact: ggerman@gmail.com


What’s Next?

MapView v0.1.0 is alpha. Future versions will include:

  • [ ] Clustering for large datasets (1000+ points)
  • [ ] Advanced styling (per-feature colors)
  • [ ] Multi-layer support
  • [ ] Streaming large GeoJSON files
  • [ ] Better error messages
  • [ ] Performance benchmarks
  • [ ] PostGIS integration helpers

The core is solid. The ecosystem around it is still evolving.


Install:

gem 'map_view'
rails generate map_view:install

Documentation: https://github.com/ggerman/map_view/blob/main/README.md


Conclusion

For 20 years, I wondered why Ruby didn’t have solid map support. The answer wasn’t “Ruby isn’t good at graphics.” The answer was: I needed to build it.

MapView is the result: a gem that takes the power of GIS libraries and makes them accessible to Rails developers who just want to render maps without JavaScript complexity.

If you’re building:

  • Delivery tracking maps
  • Store locators
  • Real estate listings
  • GIS reports
  • Marketing materials with maps

MapView might be exactly what you need.

Try it. Build with it. Let me know what you create.


About the Author

Germán Alberto Giménez Silva has been fascinated by graphics libraries since 2004. Today, he’s bringing that passion to Ruby with ruby-libgd, libgd-gis, and MapView.

Find him on GitHub or reach out at ggerman@gmail.com for questions about MapView licensing or integration.


Related Reading


Published on Ruby Stack News. Featured in Ruby Weekly.

Last updated: April 2026

Article content

Leave a comment