mirror of
https://github.com/Jermolene/TiddlyWiki5
synced 2024-09-29 07:20:47 +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:
parent
9bfe6e3a5f
commit
dbabdfce53
@ -22,11 +22,14 @@ This demo requires that the API keys needed to access external services be obtai
|
|||||||
<$geomap
|
<$geomap
|
||||||
state=<<qualify "$:/state/demo-map">>
|
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]]">
|
<$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>
|
||||||
<$list filter="[all[tiddlers+shadows]tag[$:/tags/GeoFeature]]">
|
<$list filter="[all[tiddlers+shadows]tag[$:/tags/GeoFeature]]">
|
||||||
<$geolayer json={{!!text}} color={{!!color}}/>
|
<$geolayer json={{!!text}} color={{!!color}} name={{!!caption}}/>
|
||||||
</$list>
|
</$list>
|
||||||
</$geomap>
|
</$geomap>
|
||||||
|
|
||||||
|
@ -0,0 +1,2 @@
|
|||||||
|
title: $:/tags/GeoBaseLayer
|
||||||
|
list: $:/plugins/tiddlywiki/geospatial/baselayers/openstreetmap $:/plugins/tiddlywiki/geospatial/baselayers/opentopomap $:/plugins/tiddlywiki/geospatial/baselayers/stamen-watercolor
|
@ -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
|
||||||
|
|
||||||
|
© <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>
|
7
plugins/tiddlywiki/geospatial/baselayers/opentopomap.tid
Normal file
7
plugins/tiddlywiki/geospatial/baselayers/opentopomap.tid
Normal 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: © <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors, <a href="http://viewfinderpanoramas.org">SRTM</a> | Map style: © <a href="https://opentopomap.org">OpenTopoMap</a> (<a href="https://creativecommons.org/licenses/by-sa/3.0/">CC-BY-SA</a>)
|
@ -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
|
||||||
|
|
||||||
|
© 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>.
|
18
plugins/tiddlywiki/geospatial/docs/geobaselayer.tid
Normal file
18
plugins/tiddlywiki/geospatial/docs/geobaselayer.tid
Normal 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.
|
@ -98,8 +98,10 @@ The following attributes are supported:
|
|||||||
|
|
||||||
<$testcase>
|
<$testcase>
|
||||||
<$data $compound-tiddler="$:/plugins/tiddlywiki/geospatial/tests/widgets/geomap"/>
|
<$data $compound-tiddler="$:/plugins/tiddlywiki/geospatial/tests/widgets/geomap"/>
|
||||||
|
<$data $tiddler="$:/plugins/tiddlywiki/geospatial"/>
|
||||||
</$testcase>
|
</$testcase>
|
||||||
|
|
||||||
<$testcase>
|
<$testcase>
|
||||||
<$data $compound-tiddler="$:/plugins/tiddlywiki/geospatial/tests/widgets/geomap-refresh"/>
|
<$data $compound-tiddler="$:/plugins/tiddlywiki/geospatial/tests/widgets/geomap-refresh"/>
|
||||||
|
<$data $tiddler="$:/plugins/tiddlywiki/geospatial"/>
|
||||||
</$testcase>
|
</$testcase>
|
||||||
|
@ -6,9 +6,7 @@
|
|||||||
"type": "application/javascript",
|
"type": "application/javascript",
|
||||||
"title": "$:/plugins/tiddlywiki/geospatial/leaflet.js",
|
"title": "$:/plugins/tiddlywiki/geospatial/leaflet.js",
|
||||||
"module-type": "library"
|
"module-type": "library"
|
||||||
},
|
}
|
||||||
"prefix": "",
|
|
||||||
"suffix": ""
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"file": "leaflet.css",
|
"file": "leaflet.css",
|
||||||
@ -16,9 +14,14 @@
|
|||||||
"type": "text/css",
|
"type": "text/css",
|
||||||
"title": "$:/plugins/tiddlywiki/geospatial/leaflet.css",
|
"title": "$:/plugins/tiddlywiki/geospatial/leaflet.css",
|
||||||
"tags": "[[$:/tags/Stylesheet]]"
|
"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",
|
"file": "LICENSE",
|
||||||
|
@ -2,3 +2,9 @@ title: $:/plugins/tiddlywiki/geospatial/styles
|
|||||||
tags: [[$:/tags/Stylesheet]]
|
tags: [[$:/tags/Stylesheet]]
|
||||||
|
|
||||||
\rules only filteredtranscludeinline transcludeinline macrodef macrocallinline
|
\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'>>);
|
||||||
|
|
||||||
|
}
|
17
plugins/tiddlywiki/geospatial/widgets/geobaselayer.js
Normal file
17
plugins/tiddlywiki/geospatial/widgets/geobaselayer.js
Normal 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;
|
||||||
|
|
||||||
|
})();
|
@ -61,42 +61,16 @@ GeomapWidget.prototype.render = function(parent,nextSibling) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
GeomapWidget.prototype.renderMap = function() {
|
GeomapWidget.prototype.renderMap = function() {
|
||||||
|
var self = this;
|
||||||
// Create the map
|
// Create the map
|
||||||
this.map = $tw.Leaflet.map(this.domNode);
|
this.map = $tw.Leaflet.map(this.domNode);
|
||||||
// No layers rendered
|
// No layers rendered
|
||||||
this.renderedLayers = [];
|
this.renderedLayers = [];
|
||||||
// Setup the tile layer
|
this.baseLayers = [];
|
||||||
const tiles = $tw.Leaflet.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
|
||||||
maxZoom: 19,
|
|
||||||
attribution: '© <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>'
|
|
||||||
}).addTo(this.map);
|
|
||||||
// Disable Leaflet attribution
|
// Disable Leaflet attribution
|
||||||
this.map.attributionControl.setPrefix("");
|
this.map.attributionControl.setPrefix("");
|
||||||
// Add scale
|
// Add scale
|
||||||
$tw.Leaflet.control.scale().addTo(this.map);
|
$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
|
// Listen for pan and zoom events and update the state tiddler
|
||||||
this.map.on("moveend zoomend",function(event) {
|
this.map.on("moveend zoomend",function(event) {
|
||||||
if(self.geomapStateTitle) {
|
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
|
// Make a marker cluster
|
||||||
var markers = $tw.Leaflet.markerClusterGroup({
|
var markers = $tw.Leaflet.markerClusterGroup({
|
||||||
maxClusterRadius: 40
|
maxClusterRadius: 40
|
||||||
@ -126,8 +167,10 @@ GeomapWidget.prototype.refreshMap = function() {
|
|||||||
var jsonText = widget.getAttribute("json"),
|
var jsonText = widget.getAttribute("json"),
|
||||||
geoJson = [];
|
geoJson = [];
|
||||||
if(jsonText) {
|
if(jsonText) {
|
||||||
|
// Layer is defined by JSON blob
|
||||||
geoJson = $tw.utils.parseJSONSafe(jsonText,[]);
|
geoJson = $tw.utils.parseJSONSafe(jsonText,[]);
|
||||||
} else if(widget.hasAttribute("lat") && widget.hasAttribute("long")) {
|
} else if(widget.hasAttribute("lat") && widget.hasAttribute("long")) {
|
||||||
|
// Layer is defined by lat long fields
|
||||||
var lat = $tw.utils.parseNumber(widget.getAttribute("lat","0")),
|
var lat = $tw.utils.parseNumber(widget.getAttribute("lat","0")),
|
||||||
long = $tw.utils.parseNumber(widget.getAttribute("long","0")),
|
long = $tw.utils.parseNumber(widget.getAttribute("long","0")),
|
||||||
alt = $tw.utils.parseNumber(widget.getAttribute("alt","0"));
|
alt = $tw.utils.parseNumber(widget.getAttribute("alt","0"));
|
||||||
@ -160,8 +203,24 @@ GeomapWidget.prototype.refreshMap = function() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}).addTo(self.map);
|
}).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);
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
Loading…
Reference in New Issue
Block a user