1
0
mirror of https://github.com/Jermolene/TiddlyWiki5 synced 2024-06-21 12:53:15 +00:00

Support for multiple base layers

* Make the layers control visible which allows the base layer to be chosen, and individual overlay layers to be hidden
* Add tiddlers tagged $:/tags/GeoBaseLayer to define some useful map base layers
* Add geobaselayer widget to define base layers
This commit is contained in:
jeremy@jermolene.com 2023-05-21 22:06:32 +01:00
parent 9bfe6e3a5f
commit dbabdfce53
11 changed files with 168 additions and 37 deletions

View File

@ -22,11 +22,14 @@ This demo requires that the API keys needed to access external services be obtai
<$geomap
state=<<qualify "$:/state/demo-map">>
>
<$list filter="[all[tiddlers+shadows]tag[$:/tags/GeoBaseLayer]]">
<$geobaselayer title=<<currentTiddler>>/>
</$list>
<$list filter="[all[tiddlers+shadows]tag[$:/tags/GeoMarker]]">
<$geolayer lat={{!!lat}} long={{!!long}} alt={{!!alt}} color={{!!color}}/>
<$geolayer lat={{!!lat}} long={{!!long}} alt={{!!alt}} color={{!!color}} name={{!!caption}}/>
</$list>
<$list filter="[all[tiddlers+shadows]tag[$:/tags/GeoFeature]]">
<$geolayer json={{!!text}} color={{!!color}}/>
<$geolayer json={{!!text}} color={{!!color}} name={{!!caption}}/>
</$list>
</$geomap>

View File

@ -0,0 +1,2 @@
title: $:/tags/GeoBaseLayer
list: $:/plugins/tiddlywiki/geospatial/baselayers/openstreetmap $:/plugins/tiddlywiki/geospatial/baselayers/opentopomap $:/plugins/tiddlywiki/geospatial/baselayers/stamen-watercolor

View File

@ -0,0 +1,7 @@
title: $:/plugins/tiddlywiki/geospatial/baselayers/openstreetmap
caption: OpenStreetMap
tags: $:/tags/GeoBaseLayer
tiles-url: https://tile.openstreetmap.org/{z}/{x}/{y}.png
max-zoom: 19
&copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>

View File

@ -0,0 +1,7 @@
title: $:/plugins/tiddlywiki/geospatial/baselayers/opentopomap
caption: OpenTopoMap
tags: $:/tags/GeoBaseLayer
tiles-url: https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png
max-zoom: 17
Map data: &copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors, <a href="http://viewfinderpanoramas.org">SRTM</a> | Map style: &copy; <a href="https://opentopomap.org">OpenTopoMap</a> (<a href="https://creativecommons.org/licenses/by-sa/3.0/">CC-BY-SA</a>)

View File

@ -0,0 +1,7 @@
title: $:/plugins/tiddlywiki/geospatial/baselayers/stamen-watercolor
caption: Stamen Watercolor
tags: $:/tags/GeoBaseLayer
tiles-url: https://stamen-tiles.a.ssl.fastly.net/watercolor/{z}/{x}/{y}.jpg
max-zoom: 16
&copy; Map tiles by <a href="http://stamen.com">Stamen Design</a>, under <a href="http://creativecommons.org/licenses/by/3.0">CC BY 3.0</a>. Data by <a href="http://openstreetmap.org">OpenStreetMap</a>, under <a href="http://creativecommons.org/licenses/by-sa/3.0">CC BY SA</a>.

View File

@ -0,0 +1,18 @@
title: $:/plugins/tiddlywiki/geospatial/docs/geobaselayer
caption: geobaselayer widget
tags: $:/tags/GeospatialDocs
!! `<$geobaselayer>` widget
The `<$geobaselayer>` widget is used inside the `<$geomap>` widget to define the base layers to display on the map.
The following attributes are supported:
|!Attribute |!Description |
|''title'' |Optional title of a tiddler that defines the base layer through the fields ''caption'', ''tiles-url'', ''max-zoom'' and ''text'' (the text field defines the attribution string for the base layer) |
|''name'' |Optional name for the base layer |
|''tiles-url'' |Optional templated tile server URL for the base layer |
|''max-zoom'' |Optional maximum zoom level for the base layer |
|''attribution'' |Optional attribution text for the base layer |
The base layer will only work if all four of the required items ''name'', ''tiles-url'', ''max-zoom'' and ''attribution'' must be provided, either through the base layer tiddler specified in the title attribute, or explicitly via their own attributes.

View File

@ -98,8 +98,10 @@ The following attributes are supported:
<$testcase>
<$data $compound-tiddler="$:/plugins/tiddlywiki/geospatial/tests/widgets/geomap"/>
<$data $tiddler="$:/plugins/tiddlywiki/geospatial"/>
</$testcase>
<$testcase>
<$data $compound-tiddler="$:/plugins/tiddlywiki/geospatial/tests/widgets/geomap-refresh"/>
<$data $tiddler="$:/plugins/tiddlywiki/geospatial"/>
</$testcase>

View File

@ -6,9 +6,7 @@
"type": "application/javascript",
"title": "$:/plugins/tiddlywiki/geospatial/leaflet.js",
"module-type": "library"
},
"prefix": "",
"suffix": ""
}
},
{
"file": "leaflet.css",
@ -16,9 +14,14 @@
"type": "text/css",
"title": "$:/plugins/tiddlywiki/geospatial/leaflet.css",
"tags": "[[$:/tags/Stylesheet]]"
},
"prefix": "",
"suffix": ""
}
},
{
"file": "images/layers-2x.png",
"fields": {
"type": "image/png",
"title": "$:/plugins/tiddlywiki/geospatial/leaflet/images/layers-2x.png"
}
},
{
"file": "LICENSE",

View File

@ -2,3 +2,9 @@ title: $:/plugins/tiddlywiki/geospatial/styles
tags: [[$:/tags/Stylesheet]]
\rules only filteredtranscludeinline transcludeinline macrodef macrocallinline
.leaflet-retina .leaflet-control-layers-toggle,
.leaflet-control-layers-toggle {
background-image: url(<<datauri '$:/plugins/tiddlywiki/geospatial/leaflet/images/layers-2x.png'>>);
}

View File

@ -0,0 +1,17 @@
/*\
title: $:/plugins/tiddlywiki/innerwiki/geobaselayer.js
type: application/javascript
module-type: widget
geobaselayer widget to represent a base layer for a geomap widget. Clone of the data widget
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
exports.geobaselayer = require("$:/core/modules/widgets/data.js").data;
})();

View File

@ -61,42 +61,16 @@ GeomapWidget.prototype.render = function(parent,nextSibling) {
};
GeomapWidget.prototype.renderMap = function() {
var self = this;
// Create the map
this.map = $tw.Leaflet.map(this.domNode);
// No layers rendered
this.renderedLayers = [];
// Setup the tile layer
const tiles = $tw.Leaflet.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 19,
attribution: '&copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>'
}).addTo(this.map);
this.baseLayers = [];
// Disable Leaflet attribution
this.map.attributionControl.setPrefix("");
// Add scale
$tw.Leaflet.control.scale().addTo(this.map);
};
GeomapWidget.prototype.refreshMap = function() {
var self = this;
// Remove any previously rendered layers
$tw.utils.each(this.renderedLayers,function(layer) {
self.map.removeLayer(layer);
});
this.renderedLayers = [];
// Set the position
if(!this.setMapView()) {
// Default to showing the whole world
this.map.fitWorld();
}
// Create default icon
const iconProportions = 365/560,
iconHeight = 50;
const myIcon = new $tw.Leaflet.Icon({
iconUrl: $tw.utils.makeDataUri(this.wiki.getTiddlerText("$:/plugins/tiddlywiki/geospatial/images/markers/pin"),"image/svg+xml"),
iconSize: [iconHeight * iconProportions, iconHeight], // Size of the icon
iconAnchor: [(iconHeight * iconProportions) / 2, iconHeight], // Position of the anchor within the icon
popupAnchor: [0, -iconHeight] // Position of the popup anchor relative to the icon anchor
});
// Listen for pan and zoom events and update the state tiddler
this.map.on("moveend zoomend",function(event) {
if(self.geomapStateTitle) {
@ -116,6 +90,73 @@ GeomapWidget.prototype.refreshMap = function() {
}
}
});
};
GeomapWidget.prototype.refreshMap = function() {
var self = this;
// Remove any previously rendered layers
$tw.utils.each(this.renderedLayers,function(layer) {
self.map.removeLayer(layer.layer);
});
this.renderedLayers = []; // Array of {name:,layer:}
this.renderedBaseLayers = []; // Array of {name:,layer:}
// Restore the saved map position and zoom level
if(!this.setMapView()) {
// If there was no saved position then default to showing the whole world
this.map.fitWorld();
}
// Create default icon
var iconProportions = 365/560,
iconHeight = 50;
var myIcon = new $tw.Leaflet.Icon({
iconUrl: $tw.utils.makeDataUri(this.wiki.getTiddlerText("$:/plugins/tiddlywiki/geospatial/images/markers/pin"),"image/svg+xml"),
iconSize: [iconHeight * iconProportions, iconHeight], // Size of the icon
iconAnchor: [(iconHeight * iconProportions) / 2, iconHeight], // Position of the anchor within the icon
popupAnchor: [0, -iconHeight] // Position of the popup anchor relative to the icon anchor
});
// Counter for autogenerated names
var untitledCount = 1;
// Process embedded geobaselayer widgets
function loadBaseLayer(layerInfo) {
if(layerInfo.title) {
var tiddler = self.wiki.getTiddler(layerInfo.title);
if(tiddler) {
layerInfo.name = layerInfo.name || tiddler.fields["caption"];
layerInfo.tilesUrl = layerInfo.tilesUrl || tiddler.fields["tiles-url"];
layerInfo.maxZoom = layerInfo.maxZoom || tiddler.fields["max-zoom"];
layerInfo.attribution = layerInfo.attribution || tiddler.fields.text;
}
}
var baseLayer = $tw.Leaflet.tileLayer(layerInfo.tilesUrl, {
maxZoom: layerInfo.maxZoom,
attribution: layerInfo.attribution
});
if(self.renderedBaseLayers.length === 0) {
baseLayer.addTo(self.map)
}
var name = layerInfo.name || ("Untitled " + untitledCount++);
self.renderedBaseLayers.push({name: name, layer: baseLayer});
}
this.findChildrenDataWidgets(this.contentRoot.children,"geobaselayer",function(widget) {
loadBaseLayer({
name: widget.getAttribute("name"),
title: widget.getAttribute("title"),
tilesUrl: widget.getAttribute("tiles-url"),
maxZoom: widget.getAttribute("max-zoom"),
attribution: widget.getAttribute("attribution"),
});
});
// Create the default base map if none was specified
if(this.renderedBaseLayers.length === 0) {
// Render in reverse order so that the first tagged base layer will be rendered last, and hence take priority
var baseLayerTitles = this.wiki.getTiddlersWithTag("$:/tags/GeoBaseLayer").reverse();
$tw.utils.each(baseLayerTitles,function(title) {
loadBaseLayer({title: title});
});
}
if(this.renderedBaseLayers.length === 0) {
loadBaseLayer({title: "$:/plugins/tiddlywiki/geospatial/baselayers/openstreetmap"});
}
// Make a marker cluster
var markers = $tw.Leaflet.markerClusterGroup({
maxClusterRadius: 40
@ -126,8 +167,10 @@ GeomapWidget.prototype.refreshMap = function() {
var jsonText = widget.getAttribute("json"),
geoJson = [];
if(jsonText) {
// Layer is defined by JSON blob
geoJson = $tw.utils.parseJSONSafe(jsonText,[]);
} else if(widget.hasAttribute("lat") && widget.hasAttribute("long")) {
// Layer is defined by lat long fields
var lat = $tw.utils.parseNumber(widget.getAttribute("lat","0")),
long = $tw.utils.parseNumber(widget.getAttribute("long","0")),
alt = $tw.utils.parseNumber(widget.getAttribute("alt","0"));
@ -160,8 +203,24 @@ GeomapWidget.prototype.refreshMap = function() {
}
}
}).addTo(self.map);
self.renderedLayers.push(layer);
var name = widget.getAttribute("name") || ("Untitled " + untitledCount++);
self.renderedLayers.push({name: name, layer: layer});
});
// Setup the layer control
if(this.layerControl) {
this.map.removeControl(this.layerControl);
}
var baseLayers = {};
$tw.utils.each(this.renderedBaseLayers,function(layer) {
baseLayers[layer.name] = layer.layer;
});
var overlayLayers = {};
$tw.utils.each(this.renderedLayers,function(layer) {
overlayLayers[layer.name] = layer.layer;
});
this.layerControl = $tw.Leaflet.control.layers(baseLayers,overlayLayers,{
collapsed: true
}).addTo(this.map);
};
/*