jekyll-gis-blogger
Jekyll Liquid tags and hooks for GIS storytelling blogs — maps, GPX tracks, StoryMapJS, lungkui.js scrollytelling, and sortable data tables. Zero runtime dependencies beyond Jekyll.
Installation
Add to your Jekyll site's Gemfile:
gem "jekyll-gis-blogger"
Then add to _config.yml:
plugins:
- jekyll-gis-blogger
Then bundle install.
Available Tags
{% map %} — Leaflet single-marker map
{% map lat="1.55" lon="110.33" %}
{% map lat="1.55" lon="110.33" zoom="14" caption="Kuching, Sarawak" height="500px" %}
{% map lat="1.55" lon="110.33" marker="false" %}
| Attribute | Default | Description |
|---|---|---|
lat |
required | Latitude (decimal degrees) |
lon |
required | Longitude (decimal degrees) |
zoom |
12 |
Leaflet zoom level |
height |
400px |
Map height (CSS) |
width |
100% |
Map width (CSS) |
caption |
— | HTML <figcaption> below map |
basemap |
OSM | Tile URL template |
attribution |
OSM | Tile attribution HTML |
marker |
true |
Set "false" to hide marker |
Leaflet CSS/JS loaded from CDN once per page.
{% storymap %} — StoryMapJS embed
GeoJSON (build-time transform):
{% storymap geojson="/assets/datasets/my_story.geojson" %}
CSV (build-time transform):
{% storymap csv="/assets/datasets/my_story.csv" %}
Raw StoryMapJS JSON:
{% storymap data="/assets/datasets/my_story.json" %}
Options: height="600px" width="80%" options='{"calculate_zoom":false}'
Authoring schema
| Field | Description |
|---|---|
headline |
Slide title |
text |
Slide body (HTML allowed) |
media |
Image/video/embed URL |
caption |
Media caption |
credit |
Media credit |
name |
Marker label |
zoom |
Zoom level (integer) |
icon |
Marker image URL |
line |
true/1 to draw connecting line |
date |
Optional date |
order |
Slide order (integer, default: file order) |
overview |
1/true for intro/title slide |
lat, lon |
CSV only; GeoJSON uses Point geometry |
Bundled assets
Shipped with the gem and copied into your build automatically at
assets/gis-blogger/css/storymap.css and assets/gis-blogger/js/storymap-min.js.
Nothing to copy by hand.
{% lungkui %} — lungkui.js MapLibre scrollytelling
GeoJSON (build-time transform):
{% lungkui geojson="/assets/datasets/my_story.geojson" %}
Raw config JSON:
{% lungkui config="/assets/datasets/my_story.lungkui.json" %}
Options: height="600px" width="100%" mode="deck"|"scroll" center="113.9,3.5" zoom="6" accent="#e4572e" basemap="..."
Mode deck (default) uses arrow keys/click — no page scroll needed. Mode scroll drives the map from page scroll position.
Authoring schema
Same GeoJSON field names as storymap (headline, text, media, caption, credit, zoom, order, overview). Coordinates from Point geometry.
Bundled assets
assets/gis-blogger/js/lungkui.js and assets/gis-blogger/css/lungkui.css ship
with the gem and are copied into your build automatically. MapLibre GL JS is
loaded from CDN. Nothing to copy by hand.
{% gpx_track %} — GPX track + elevation profile
{% gpx_track file="/assets/tracks/hike.gpx" %}
{% gpx_track file="/assets/tracks/hike.gpx" height="500px" color="#e4572e" caption="Day 1 survey" %}
{% gpx_track file="/assets/tracks/hike.gpx" profile="false" %}
| Attribute | Default | Description |
|---|---|---|
file |
required | Path to GPX file |
height |
400px |
Map height (CSS) |
width |
100% |
Map width (CSS) |
color |
#e4572e |
Track line color |
caption |
— | HTML <figcaption> |
profile |
true |
Set "false" to hide elevation SVG |
Renders: Leaflet map with track polyline + start/end markers, inline SVG elevation profile, and stats (distance, gain, loss, elevation range).
{% datatable %} — Sortable table from GeoJSON/CSV
{% datatable src="/assets/data/sites.geojson" %}
{% datatable src="/assets/data/samples.csv" caption="Field samples" %}
{% datatable src="/assets/data/sites.geojson" cols="name,date,elevation" sort="date" %}
| Attribute | Default | Description |
|---|---|---|
src |
required | Path to .geojson or .csv |
caption |
— | HTML <caption> |
cols |
all | Comma-separated columns to show |
sort |
first col | Column to pre-sort by |
Click column headers to sort ascending/descending. Client-side vanilla JS.
Automatic: Responsive table wrapper
All <table> elements in posts/pages are automatically wrapped in
<div class="table-wrapper"> for mobile scroll. Tables already inside
.dt-scroll or .table-wrapper are skipped.
Requirements
- Jekyll ~> 4.0
- Ruby >= 2.7
License
MIT — see LICENSE.