Skip to content

Commit 2dfecd7

Browse files
committed
add support for geojson in the map component
this adds support for new geometries, including polygons Closes #119
1 parent 65a7cf3 commit 2dfecd7

File tree

11 files changed

+86
-40
lines changed

11 files changed

+86
-40
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
- Fix a bug where [cookie](https://sql.ophir.dev/documentation.sql?component=cookie#component) removal set the cookie value to the empty string instead of removing the cookie completely.
1313
- Support form submission using the [button](https://sql.ophir.dev/documentation.sql?component=button#component) component using its new `form` property. This allows you to create a form with multiple submit buttons that submit the form to different targets.
1414
- Custom icons and colors for markers in the [map](https://sql.ophir.dev/documentation.sql?component=map#component) component.
15+
- Add support for GeoJSON in the [map](https://sql.ophir.dev/documentation.sql?component=map#component) component. This makes it much more generic and allows you to display any kind of geographic data, including areas, on a map very easily. This plays nicely with PostGIS and Spatialite which can return GeoJSON directly from SQL queries.
1516

1617
## 0.15.0 (2023-10-29)
1718
- New function: [`sqlpage.path`](https://sql.ophir.dev/functions.sql?function=path#function) to get the path of the current page.

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,11 @@ To run on a server, you can use [the docker image](https://hub.docker.com/r/lova
128128
- Optionally, you can also mount a directory containing sqlpage's configuration file,
129129
custom components, and migrations
130130
(see [configuration.md](./configuration.md)) to `/etc/sqlpage` in the container.
131+
- If you want to build your own docker image, taking the raw sqlpage image as a base is not recommended, since it is extremely stripped down and probably won't contain the dependencies you need. Instead, you can take debian as a base and simply copy the sqlpage binary from the official image to your own image:
132+
- ```Dockerfile
133+
FROM debian:stable-slim
134+
COPY --from=lovasoa/sqlpage:main /usr/local/bin/sqlpage /usr/local/bin/sqlpage
135+
```
131136

132137
### On Mac OS, with homebrew
133138

examples/PostGIS - using sqlpage with geographic data/Dockerfile

Lines changed: 0 additions & 14 deletions
This file was deleted.
Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,10 @@
1-
FROM debian:12-slim
1+
FROM debian:stable-slim
22

3-
WORKDIR /var/www
4-
5-
ADD https://github.com/lovasoa/SQLpage/releases/download/v0.10.1/sqlpage-linux.tgz .
3+
COPY --from=lovasoa/sqlpage:main /usr/local/bin/sqlpage /usr/local/bin/sqlpage
64

75
RUN apt-get update && \
8-
apt-get -y install sqlite3 libsqlite3-mod-spatialite && \
9-
tar xvzf sqlpage-linux.tgz && \
10-
rm sqlpage-linux.tgz
6+
apt-get -y install libsqlite3-mod-spatialite
117

128
COPY . .
139

14-
CMD ["sqlite3"]
10+
CMD ["sqlpage"]

examples/make a geographic data application using sqlite extensions/index.sql

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@ SELECT 'map' as component,
1616
SELECT title,
1717
ST_Y(geom) AS latitude,
1818
ST_X(geom) AS longitude,
19-
'point.sql?id=' || id as link
19+
'point.sql?id=' || id as link,
20+
'red' as color,
21+
'world-pin' as icon
2022
FROM spatial_data;
2123

2224

examples/official-site/custom_components.sql

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ and SQLPage adds a few more:
109109
- `icon_img`: generate an svg icon from a *tabler* icon name
110110
- `markdown`: renders markdown text
111111
- `each_row`: iterates over the rows of a query result
112+
- `typeof`: returns the type of a value (`string`, `number`, `boolean`, `object`, `array`, `null`)
112113
113114
## Overwriting the default components
114115

examples/official-site/documentation.sql

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,14 +114,17 @@ select
114114
WHEN 'true' THEN 'TRUE'
115115
WHEN 'false' THEN 'FALSE'
116116
WHEN 'null' THEN 'NULL'
117+
WHEN 'object' THEN 'JSON(' || quote(t.value) || ')'
118+
WHEN 'array' THEN 'JSON(' || quote(t.value) || ')'
117119
ELSE quote(t.value)
118120
END as val,
119121
CASE parent.fullkey
120122
WHEN '$' THEN t.key
121123
ELSE parent.key
122124
END as key
123125
from t inner join t parent on parent.id = t.parent
124-
where t.atom is not null
126+
where ((parent.fullkey = '$' and t.type != 'array')
127+
or (parent.type = 'array' and parent.path = '$'))
125128
),
126129
key_val_padding as (select
127130
CASE WHEN key LIKE '% %' THEN format('"%s"', replace(key, '"', '""')) ELSE key END as key,

examples/official-site/sqlpage/migrations/10_map.sql

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -41,47 +41,47 @@ VALUES (
4141
(
4242
'map',
4343
'latitude',
44-
'Latitude of the marker',
44+
'Latitude of the marker. Required if geojson is not set.',
4545
'REAL',
4646
FALSE,
4747
FALSE
4848
),
4949
(
5050
'map',
5151
'longitude',
52-
'Longitude of the marker',
52+
'Longitude of the marker. Required if geojson is not set.',
5353
'REAL',
5454
FALSE,
5555
FALSE
5656
),
5757
(
5858
'map',
5959
'title',
60-
'Title of the marker',
60+
'Title of the marker, displayed on hover and in the tooltip when the marker is clicked.',
6161
'TEXT',
6262
FALSE,
6363
TRUE
6464
),
6565
(
6666
'map',
6767
'link',
68-
'A link to associate to the marker''s title',
68+
'A link to associate to the marker''s title. If set, the marker tooltip''s title will be clickable and will open the link.',
6969
'TEXT',
7070
FALSE,
7171
TRUE
7272
),
7373
(
7474
'map',
7575
'description',
76-
'Plain text description of the marker, as plain text',
76+
'Plain text description of the marker, to be displayed in a tooltip when the marker is clicked.',
7777
'TEXT',
7878
FALSE,
7979
TRUE
8080
),
8181
(
8282
'map',
8383
'description_md',
84-
'Description of the marker, in markdown',
84+
'Description of the marker, in markdown, rendered in a tooltip when the marker is clicked.',
8585
'TEXT',
8686
FALSE,
8787
TRUE
@@ -101,6 +101,14 @@ VALUES (
101101
'COLOR',
102102
FALSE,
103103
TRUE
104+
),
105+
(
106+
'map',
107+
'geojson',
108+
'A GeoJSON geometry (line, a polygon, ...) to display on the map. Can be styled using geojson properties using the name of leaflet path options.',
109+
'JSON',
110+
FALSE,
111+
TRUE
104112
)
105113
;
106114
-- Insert an example usage of the map component into the example table
@@ -114,12 +122,17 @@ VALUES (
114122
),
115123
(
116124
'map',
117-
'Map of Paris',
125+
'Map of Paris.
126+
Illustrates the use custom styling, and [GeoJSON](https://en.wikipedia.org/wiki/GeoJSON) to display a line between two points.
127+
In a real-world scenario, the GeoJSON could be generated by calling PostGIS''s
128+
[`ST_AsGeoJSON`](https://postgis.net/docs/ST_AsGeoJSON.html) or
129+
Spatialite''s [`AsGeoJSON`](https://www.gaia-gis.it/gaia-sins/spatialite-sql-5.1.0.html#p3misc) functions on a geometry column.',
118130
JSON(
119131
'[
120132
{ "component": "map", "title": "Paris", "zoom": 11, "latitude": 48.85, "longitude": 2.34, "height": 400 },
121133
{ "title": "Notre Dame", "icon": "building-castle", "color": "indigo", "latitude": 48.8530, "longitude": 2.3498, "description_md": "A beautiful cathedral.", "link": "https://en.wikipedia.org/wiki/Notre-Dame_de_Paris" },
122-
{ "title": "Eiffel Tower", "icon": "tower", "color": "yellow", "latitude": 48.8584, "longitude": 2.2945, "description_md": "A tall tower. [Wikipedia](https://en.wikipedia.org/wiki/Eiffel_Tower)" }
123-
]'
134+
{ "title": "Eiffel Tower", "icon": "tower", "color": "yellow", "latitude": 48.8584, "longitude": 2.2945, "description_md": "A tall tower. [Wikipedia](https://en.wikipedia.org/wiki/Eiffel_Tower)" },
135+
{ "title": "Tower to Cathedral", "geojson": {"type": "LineString", "coordinates": [[2.2945, 48.8584], [2.3498, 48.8530]]}, "color": "teal", "description": "A nice 45 minutes walk." }
136+
]'
124137
)
125138
);

sqlpage/sqlpage.js

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -54,25 +54,46 @@ function sqlpage_map() {
5454
}
5555
}
5656
function addMarker(marker_elem, map) {
57-
const coords = marker_elem.dataset.coords.split(",").map(c => parseFloat(c));
57+
const { dataset } = marker_elem;
5858
const options = {
59+
color: marker_elem.dataset.color,
5960
title: marker_elem.getElementsByTagName("h3")[0].textContent.trim(),
6061
};
61-
const color = marker_elem.dataset.color;
62+
const marker =
63+
dataset.coords ? createMarker(marker_elem, options)
64+
: createGeoJSONMarker(marker_elem, options);
65+
marker.addTo(map).bindPopup(marker_elem);
66+
}
67+
function createMarker(marker_elem, options) {
68+
const coords = marker_elem.dataset.coords.split(",").map(c => parseFloat(c));
6269
const icon_obj = marker_elem.getElementsByClassName("mapicon")[0];
6370
if (icon_obj) {
6471
options.icon = L.divIcon({
6572
html: icon_obj,
66-
className: `border-0 bg-${color || 'primary'} bg-gradient text-white rounded-circle p-2 shadow`,
73+
className: `border-0 bg-${options.color || 'primary'} bg-gradient text-white rounded-circle p-2 shadow`,
6774
iconSize: [42, 42],
68-
iconAnchor: [21, 5],
75+
iconAnchor: [21, 21],
6976
});
7077
}
71-
const marker = L.marker(coords, options).addTo(map);
72-
marker.bindPopup(marker_elem);
78+
return L.marker(coords, options);
79+
}
80+
function createGeoJSONMarker(marker_elem, options) {
81+
let geojson = JSON.parse(marker_elem.dataset.geojson);
82+
if (options.color) {
83+
options.color = get_tabler_color(options.color) || options.color;
84+
}
85+
function style({ properties }) {
86+
if (typeof properties !== "object") return options;
87+
return {...options, ...properties};
88+
}
89+
return L.geoJSON(geojson, { style });
7390
}
7491
}
7592

93+
function get_tabler_color(name) {
94+
return getComputedStyle(document.documentElement).getPropertyValue('--tblr-' + name);
95+
}
96+
7697
document.addEventListener('DOMContentLoaded', function () {
7798
sqlpage_table();
7899
sqlpage_chart();

sqlpage/templates/map.handlebars

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,15 @@
1717
</div>
1818
<div class="d-none" hidden>
1919
{{~#each_row~}}
20-
<div class="marker" data-coords="{{latitude}},{{longitude}}" {{#if color}}data-color="{{color}}"{{/if}}>
20+
<div class="marker"
21+
{{#if latitude}}data-coords="{{latitude}},{{longitude}}"{{/if}}
22+
{{#if color}}data-color="{{color}}"{{/if}}
23+
{{#if geojson}}data-geojson="
24+
{{~#if eq (typeof geojson) 'string'}}{{geojson}}
25+
{{~else~}}{{stringify geojson}}
26+
{{~/if~}}
27+
"{{/if}}
28+
>
2129
<h3>
2230
{{~#if icon~}}
2331
<span class="mapicon">{{~icon_img icon~}}</span>

0 commit comments

Comments
 (0)