GeoJSON template engine with integrated multimedia viewer
English | 日本語
The project is named after Qu Yuan (屈原), a Chinese poet and politician from the 4th century BC.
A template library that generates markers for GeoJSON and HTML to display in popups when clicked, based on attributes in the GeoJSON properties.
It also provides a slider viewer for displaying multimedia content (images, panoramas, videos) within popups as a common use case.
- Template syntax using Nunjucks
- Template processing for each GeoJSON feature with properties as root
- Multiple template key definitions (marker icons, popup HTML, etc.)
- Processing results stored in each feature's result object
- Web Components-based viewer implementation
- Thumbnail display with swiper and integration with various viewers
- Supported formats:
- Images
- 360-degree panoramic images
- YouTube videos
- Formats in development:
- Textured 3D polygon models
- 3D Gaussian Splatting
npm install @c4h/quyuanor
pnpm add @c4h/quyuanimport { Quyuan } from '@c4h/quyuan';
const geojson = {
type: "FeatureCollection",
features: [{
type: "Feature",
properties: {
name: "Sample Location",
type: "cultural",
images: [
{ path: "image1.jpg", type: "image", description: "Sample image" },
{ path: "pano1.jpg", type: "panorama", description: "360-degree image" }
]
},
geometry: {
type: "Point",
coordinates: [139.7, 35.6]
}
}]
};
const templates = {
// Template for icon selection
icon: "{% if type == 'cultural' %}cultural.png{% else %}default.png{% endif %}",
// Template for popup HTML generation
html: `
<div class="popup-content">
<h3>{{ name }}</h3>
<qy-swiper style="height:200px;">
{% for image in images %}
<qy-swiper-slide
image-url="{{ image.path }}"
image-type="{{ image.type }}"
caption="{{ image.description }}">
</qy-swiper-slide>
{% endfor %}
</qy-swiper>
</div>
`
};
const result = Quyuan.templateExtractor({ geojson, templates });
// Processing results are stored in each feature's result objectimport L from 'leaflet';
const map = L.map('map').setView([35.6, 139.7], 13);
result.features.forEach(feature => {
if (feature.geometry) {
L.marker(feature.geometry.coordinates.slice().reverse(), {
icon: L.icon({
iconUrl: feature.result.icon,
iconSize: [32, 32],
iconAnchor: [16, 32],
popupAnchor: [0, -32]
})
})
.bindPopup(feature.result.html)
.addTo(map);
}
});import { Feature } from 'ol';
import { Point } from 'ol/geom';
import { Style, Icon } from 'ol/style';
import { fromLonLat } from 'ol/proj';
import Overlay from 'ol/Overlay';
result.features.forEach(feature => {
if (feature.geometry) {
const point = new Feature({
geometry: new Point(fromLonLat(feature.geometry.coordinates))
});
point.setStyle(new Style({
image: new Icon({
src: feature.result.icon,
scale: 0.5
})
}));
vectorSource.addFeature(point);
// Popup configuration
point.set('popupContent', feature.result.html);
}
});import maplibregl from 'maplibre-gl';
const map = new maplibregl.Map({
container: 'map',
style: 'https://tile.openstreetmap.jp/styles/osm-bright-ja/style.json',
center: [139.7, 35.6],
zoom: 13
});
result.features.forEach(feature => {
if (feature.geometry) {
const popup = new maplibregl.Popup()
.setHTML(feature.result.html);
const el = document.createElement('div');
el.style.backgroundImage = `url(${feature.result.icon})`;
el.style.width = '32px';
el.style.height = '32px';
el.style.backgroundSize = 'contain';
el.style.cursor = 'pointer';
new maplibregl.Marker(el)
.setLngLat(feature.geometry.coordinates)
.setPopup(popup)
.addTo(map);
}
});Quyuan provides the following Web Components:
Component for displaying multimedia content in a slider
Attributes:
style: CSS style (height specification recommended)
Component defining each slide within the slider
Attributes:
image-url: Image/video URLimage-type: Media type ("image", "panorama", "youtube")caption: Caption stringthumbnail-url: Thumbnail image URL (uses image-url if omitted)
Fullscreen viewer component (automatically called from qy-swiper)
- Chrome/Edge (latest)
- Firefox (latest)
- Safari (latest)
Works on modern browsers that support Web Components.
# Install dependencies
pnpm install
# Start development server
pnpm run dev
# Build
pnpm run build
# Run tests
pnpm test
# Run E2E tests
pnpm run test:e2eMIT License
Copyright (c) 2024 Code for History
- Kohei Otsuka (@kochizufan)
- Code for History
We welcome your contributions!
- Report bugs or request features in Issues
- Pull requests are welcome
- Questions and discussions in Discussions
- Fork this repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Create a Pull Request
- Chuci (楚辞) - Multimedia viewer components separated from Quyuan
- Maplat - Historical map platform