diff --git a/assets/maps/contao-leaflet.js b/assets/maps/contao-leaflet.js
index fd25c46..c6ac251 100644
--- a/assets/maps/contao-leaflet.js
+++ b/assets/maps/contao-leaflet.js
@@ -1 +1 @@
-L.Contao=L.Class.extend({includes:L.Mixin.Events,statics:{ATTRIBUTION:' | netzmacht'},maps:{},icons:{},initialize:function(){L.Icon.Default.imagePath="assets/leaflet/libs/leaflet/images/",this.setGeoJsonListeners(L.GeoJSON)},addMap:function(t,o){return this.maps[t]=o,this.fire("map:added",{id:t,map:o}),this},getMap:function(t){return"undefined"==typeof this.maps[t]?null:this.maps[t]},addIcon:function(t,o){return this.icons[t]=o,this.fire("icon:added",{id:t,icon:o}),this},loadIcons:function(t){for(var o=0;onetzmacht'},maps:{},icons:{},initialize:function(){L.Icon.Default.imagePath="assets/leaflet/libs/leaflet/images/",this.setGeoJsonListeners(L.GeoJSON)},addMap:function(t,o){return this.maps[t]=o,this.fire("map:added",{id:t,map:o}),this},getMap:function(t){return"undefined"==typeof this.maps[t]?null:this.maps[t]},addIcon:function(t,o){return this.icons[t]=o,this.fire("icon:added",{id:t,icon:o}),this},loadIcons:function(t){for(var o=0;o=200&&t<300||304===t}function i(){void 0===a.status||n(a.status)?o.call(a,null,a):o.call(a,a,null)}var s=!1;if("undefined"==typeof window.XMLHttpRequest)return o(Error("Browser not supported"));if("undefined"==typeof e){var r=t.match(/^\s*https?:\/\/[^\/]*/);e=r&&r[0]!==location.protocol+"//"+location.hostname+(location.port?":"+location.port:"")}var a=new window.XMLHttpRequest;if(e&&!("withCredentials"in a)){a=new window.XDomainRequest;var p=o;o=function(){if(s)p.apply(this,arguments);else{var t=this,o=arguments;setTimeout(function(){p.apply(t,o)},0)}}}return"onload"in a?a.onload=i:a.onreadystatechange=function(){4===a.readyState&&i()},a.onerror=function(t){o.call(this,t||!0,null),o=function(){}},a.onprogress=function(){},a.ontimeout=function(t){o.call(this,t,null),o=function(){}},a.onabort=function(t){o.call(this,t,null),o=function(){}},a.open("GET",t,!0),a.send(null),s=!0,a}});
\ No newline at end of file
diff --git a/assets/maps/src/Mixin.Map.js b/assets/maps/src/Mixin.Map.js
index e1b8268..110ff44 100644
--- a/assets/maps/src/Mixin.Map.js
+++ b/assets/maps/src/Mixin.Map.js
@@ -30,23 +30,31 @@ L.Map.include({
}
if (this._dynamicBounds) {
- options = {};
-
- if (this.options.boundsPadding) {
- options.padding = this.options.boundsPadding;
- } else {
- if (this.options.boundsPaddingTopLeft) {
- options.paddingTopLeft = this.options.boundsPaddingTopLeft;
- }
- if (this.options.boundsPaddingBottomRight) {
- options.paddingBottomRight = this.options.boundsPaddingBottomRight;
- }
- }
-
- this.fitBounds(this._dynamicBounds, options);
+ this.fitBounds(this._dynamicBounds, this.getBoundsOptions());
}
},
+ /**
+ * Get the bounds optons
+ * @returns {{}}
+ */
+ getBoundsOptions: function () {
+ options = {};
+
+ if (this.options.boundsPadding) {
+ options.padding = this.options.boundsPadding;
+ } else {
+ if (this.options.boundsPaddingTopLeft) {
+ options.paddingTopLeft = this.options.boundsPaddingTopLeft;
+ }
+ if (this.options.boundsPaddingBottomRight) {
+ options.paddingBottomRight = this.options.boundsPaddingBottomRight;
+ }
+ }
+
+ return options;
+ },
+
/**
* Scan recursively for bounds in a layer and extend _dynamicBounds if any found.
*
diff --git a/assets/maps/src/OverpassLayer.js b/assets/maps/src/OverpassLayer.js
new file mode 100644
index 0000000..2018903
--- /dev/null
+++ b/assets/maps/src/OverpassLayer.js
@@ -0,0 +1,215 @@
+/**
+ * Get the bounds as overpass bbox string.
+ *
+ * @returns {string}
+ */
+L.LatLngBounds.prototype.toOverpassBBoxString = function () {
+ var a = this._southWest,
+ b = this._northEast;
+
+ return [a.lat, a.lng, b.lat, b.lng].join(",");
+};
+
+/**
+ * Implementation of the overpass layer. Heavily inspired by
+ * https://github.com/kartenkarsten/leaflet-layer-overpass.
+ */
+L.OverPassLayer = L.FeatureGroup.extend({
+ options: {
+ minZoom: 0,
+ endpoint: '//overpass-api.de/api/',
+ query: '(node(BBOX)[organic];node(BBOX)[second_hand];);out qt;'
+ },
+ /**
+ * Initialize the layer.
+ *
+ * @param options
+ */
+ initialize: function (options) {
+ L.Util.setOptions(this, options);
+
+ this.options.pointToLayer = this.pointToLayer;
+ this.options.onEachFeature = this.onEachFeature;
+ this.options.dynamicLoad = this.options.query.match(/BBOX/g) ? true : false;
+
+ this._layer = L.geoJson();
+ this._layers = {};
+
+ this.addLayer(this._layer);
+ },
+ /**
+ * Refresh the data of the layer.
+ *
+ * TODO: Implement some caching.
+ */
+ refreshData: function () {
+ if (this._map.getZoom() < this.options.minZoom) {
+ return;
+ }
+
+ var bounds = this._map.getBounds().toOverpassBBoxString();
+ var query = this.options.query.replace(/(BBOX)/g, bounds);
+ var url = this.options.endpoint + "interpreter?data=[out:json];" + query;
+
+ this._map.fire('dataloading', {layer: this});
+
+ this.request(url, function (error, response) {
+ var data = JSON.parse(response.response);
+ var features = osmtogeojson(data);
+ var layer = L.geoJson(features, {
+ pointToLayer: this.options.pointToLayer.bind(this),
+ onEachFeature: this.options.onEachFeature.bind(this)
+ });
+
+ this.addLayer(layer);
+ this.removeLayer(this._layer);
+ this._layer = layer;
+
+ if (this.options.boundsMode === 'extend' && layer.getBounds().isValid()) {
+ var bounds = this._map.getBounds();
+ bounds = bounds.extend(layer.getBounds());
+
+ this._map.fitBounds(bounds, this._map.getBoundsOptions());
+ }
+
+ this._map.fire('dataload', {layer: this});
+ }.bind(this));
+ },
+ /**
+ * @param map
+ */
+ onAdd: function (map) {
+ if (this.options.boundsMode === 'fit' && this.options.dynamicLoad) {
+ map.on('moveend', this.refreshData, this);
+ }
+
+ this.refreshData();
+ },
+ pointToLayer: function (feature, latlng) {
+ var type = 'marker';
+ var marker = L.marker(latlng, feature.properties.options);
+
+ if (feature.properties) {
+ if (feature.properties.radius) {
+ marker.setRadius(feature.properties.radius);
+ }
+
+ if (feature.properties.icon) {
+ var icon = this._map.getIcon(feature.properties.icon);
+
+ if (icon) {
+ marker.setIcon(icon);
+ }
+ }
+
+ L.contao.bindPopupFromFeature(marker, feature);
+ }
+
+ this._map.fire('point:added', {marker: marker, feature: feature, latlng: latlng, type: type});
+
+ return marker;
+ },
+ onEachFeature: function (feature, layer) {
+ if (feature.properties) {
+ L.Util.setOptions(layer, feature.properties.options);
+
+ L.contao.bindPopupFromFeature(layer, feature);
+
+ this._map.fire('feature:added', {feature: feature, layer: layer});
+ }
+ },
+ /**
+ * Make an ajax request. Clone of corslite from MapQuest.
+ */
+ request: function (url, callback, cors) {
+ var sent = false;
+
+ if (typeof window.XMLHttpRequest === 'undefined') {
+ return callback(Error('Browser not supported'));
+ }
+
+ if (typeof cors === 'undefined') {
+ var m = url.match(/^\s*https?:\/\/[^\/]*/);
+ cors = m && (m[0] !== location.protocol + '//' + location.hostname +
+ (location.port ? ':' + location.port : ''));
+ }
+
+ var x = new window.XMLHttpRequest();
+
+ function isSuccessful(status) {
+ return status >= 200 && status < 300 || status === 304;
+ }
+
+ if (cors && !('withCredentials' in x)) {
+ // IE8-9
+ x = new window.XDomainRequest();
+
+ // Ensure callback is never called synchronously, i.e., before
+ // x.send() returns (this has been observed in the wild).
+ // See https://github.com/mapbox/mapbox.js/issues/472
+ var original = callback;
+ callback = function() {
+ if (sent) {
+ original.apply(this, arguments);
+ } else {
+ var that = this, args = arguments;
+ setTimeout(function() {
+ original.apply(that, args);
+ }, 0);
+ }
+ }
+ }
+
+ function loaded() {
+ if (
+ // XDomainRequest
+ x.status === undefined ||
+ // modern browsers
+ isSuccessful(x.status)) callback.call(x, null, x);
+ else callback.call(x, x, null);
+ }
+
+ // Both `onreadystatechange` and `onload` can fire. `onreadystatechange`
+ // has [been supported for longer](http://stackoverflow.com/a/9181508/229001).
+ if ('onload' in x) {
+ x.onload = loaded;
+ } else {
+ x.onreadystatechange = function readystate() {
+ if (x.readyState === 4) {
+ loaded();
+ }
+ };
+ }
+
+ // Call the callback with the XMLHttpRequest object as an error and prevent
+ // it from ever being called again by reassigning it to `noop`
+ x.onerror = function error(evt) {
+ // XDomainRequest provides no evt parameter
+ callback.call(this, evt || true, null);
+ callback = function() { };
+ };
+
+ // IE9 must have onprogress be set to a unique function.
+ x.onprogress = function() { };
+
+ x.ontimeout = function(evt) {
+ callback.call(this, evt, null);
+ callback = function() { };
+ };
+
+ x.onabort = function(evt) {
+ callback.call(this, evt, null);
+ callback = function() { };
+ };
+
+ // GET is the only supported HTTP Verb by XDomainRequest and is the
+ // only one supported here.
+ x.open('GET', url, true);
+
+ // Send the request. Sending data is not supported.
+ x.send(null);
+ sent = true;
+
+ return x;
+ }
+});
diff --git a/module/config/config.php b/module/config/config.php
index 7250c30..11c0100 100644
--- a/module/config/config.php
+++ b/module/config/config.php
@@ -280,7 +280,11 @@ $GLOBALS['LEAFLET_LAYERS'] = array
}
return $label;
- }
+ },
+ 'boundsMode' => array(
+ 'extend' => true,
+ 'fit' => true,
+ ),
),
);
diff --git a/module/dca/tl_leaflet_layer.php b/module/dca/tl_leaflet_layer.php
index 7f867b4..fa7710a 100644
--- a/module/dca/tl_leaflet_layer.php
+++ b/module/dca/tl_leaflet_layer.php
@@ -219,13 +219,11 @@ $GLOBALS['TL_DCA']['tl_leaflet_layer'] = array
'overpassQuery',
'overpassEndpoint',
'minZoom',
- 'overpassCallback',
+ 'boundsMode'
),
'+expert' => array(
- 'minZoomIndicatorPosition',
- 'debug',
- 'minZoomIndicatorMessage',
- 'minZoomIndicatorMessageNoLayer',
+ 'onEachFeature',
+ 'pointToLayer',
),
),
),
diff --git a/src/Netzmacht/Contao/Leaflet/Definition/Layer/OverpassLayer.php b/src/Netzmacht/Contao/Leaflet/Definition/Layer/OverpassLayer.php
new file mode 100644
index 0000000..cd94ff7
--- /dev/null
+++ b/src/Netzmacht/Contao/Leaflet/Definition/Layer/OverpassLayer.php
@@ -0,0 +1,190 @@
+
+ * @copyright 2016 netzmacht David Molineus. All rights reserved.
+ * @filesource
+ *
+ */
+
+namespace Netzmacht\Contao\Leaflet\Definition\Layer;
+
+use Netzmacht\JavascriptBuilder\Encoder;
+use Netzmacht\JavascriptBuilder\Type\AnonymousFunction;
+use Netzmacht\JavascriptBuilder\Type\ConvertsToJavascript;
+use Netzmacht\JavascriptBuilder\Type\Expression;
+use Netzmacht\LeafletPHP\Definition\AbstractLayer;
+use Netzmacht\LeafletPHP\Definition\HasOptions;
+use Netzmacht\LeafletPHP\Encoder\EncodeHelperTrait;
+
+/**
+ * Class OverpassLayer provides implementation of https://github.com/kartenkarsten/leaflet-layer-overpass.
+ *
+ * @package Netzmacht\LeafletPHP\Plugins\OverpassLayer
+ */
+class OverpassLayer extends AbstractLayer implements HasOptions, ConvertsToJavascript
+{
+ use EncodeHelperTrait;
+
+ /**
+ * {@inheritdoc}
+ */
+ public static function getType()
+ {
+ return 'OverpassLayer';
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public static function getRequiredLibraries()
+ {
+ $libs = parent::getRequiredLibraries();
+ $libs[] = 'osmtogeojson';
+
+ return $libs;
+ }
+
+ /**
+ * OverpassLayer constructor.
+ *
+ * @param string $identifier Indicator of the layer.
+ * @param array $options Options.
+ */
+ public function __construct($identifier, array $options = [])
+ {
+ parent::__construct($identifier);
+
+ $this->setOptions($options);
+ }
+
+ /**
+ * Set the debug mode.
+ *
+ * @param bool $debug Debug mode.
+ *
+ * @return $this
+ */
+ public function setDebug($debug)
+ {
+ return $this->setOption('debug', (bool) $debug);
+ }
+
+ /**
+ * Get debug mode.
+ *
+ * @return bool
+ */
+ public function getDebug()
+ {
+ return $this->getOption('debug', false);
+ }
+
+ /**
+ * Set the query.
+ *
+ * @param string $query Query.
+ *
+ * @return $this
+ */
+ public function setQuery($query)
+ {
+ return $this->setOption('query', $query);
+ }
+
+ /**
+ * Get query.
+ *
+ * @return bool
+ */
+ public function getQuery()
+ {
+ return $this->getOption('query', '(node(BBOX)[organic];node(BBOX)[second_hand];);out qt;');
+ }
+
+ /**
+ * Set the endpoint.
+ *
+ * @param string $endpoint Endpoint.
+ *
+ * @return $this
+ */
+ public function setEndpoint($endpoint)
+ {
+ return $this->setOption('endpoint', $endpoint);
+ }
+
+ /**
+ * Get endpoint.
+ *
+ * @return bool
+ */
+ public function getEndpoint()
+ {
+ return $this->getOption('endpoint', '//overpass-api.de/api/');
+ }
+
+ /**
+ * Set point to layer function.
+ *
+ * @param Expression|AnonymousFunction $function The function callback.
+ *
+ * @return $this
+ */
+ public function setPointToLayer($function)
+ {
+ return $this->setOption('pointToLayer', $function);
+ }
+
+ /**
+ * Set on each feature function.
+ *
+ * @param Expression|AnonymousFunction $function The function callback.
+ *
+ * @return $this
+ */
+ public function setOnEachFeature($function)
+ {
+ return $this->setOption('onEachFeature', $function);
+ }
+
+ /**
+ * Set the minZoom.
+ *
+ * @param int $minZoom MinZoom.
+ *
+ * @return $this
+ */
+ public function setMinZoom($minZoom)
+ {
+ return $this->setOption('minZoom', (int) $minZoom);
+ }
+
+ /**
+ * Get minZoom.
+ *
+ * @return bool
+ */
+ public function getMinZoom()
+ {
+ return $this->getOption('minZoom', 15);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function encode(Encoder $encoder, $flags = null)
+ {
+ $buffer = sprintf (
+ '%s = new L.OverPassLayer(%s)%s',
+ $encoder->encodeReference($this),
+ $encoder->encodeArray($this->getOptions(), JSON_FORCE_OBJECT),
+ $encoder->close($flags)
+ );
+
+ $buffer .= $this->encodeMethodCalls($this->getMethodCalls(), $encoder, $flags);
+
+ return $buffer;
+ }
+}
diff --git a/src/Netzmacht/Contao/Leaflet/Mapper/Layer/OverpassLayerMapper.php b/src/Netzmacht/Contao/Leaflet/Mapper/Layer/OverpassLayerMapper.php
index afd093c..36fb44d 100644
--- a/src/Netzmacht/Contao/Leaflet/Mapper/Layer/OverpassLayerMapper.php
+++ b/src/Netzmacht/Contao/Leaflet/Mapper/Layer/OverpassLayerMapper.php
@@ -10,12 +10,12 @@
namespace Netzmacht\Contao\Leaflet\Mapper\Layer;
+use Model;
+use Netzmacht\Contao\Leaflet\Definition\Layer\OverpassLayer;
use Netzmacht\Contao\Leaflet\Filter\Filter;
use Netzmacht\Contao\Leaflet\Mapper\DefinitionMapper;
-use Netzmacht\Contao\Leaflet\Mapper\OptionsBuilder;
use Netzmacht\JavascriptBuilder\Type\Expression;
use Netzmacht\LeafletPHP\Definition;
-use Netzmacht\LeafletPHP\Plugins\OverpassLayer\OverpassLayer;
/**
* Class OverpassLayerMapper
@@ -36,7 +36,7 @@ class OverpassLayerMapper extends AbstractLayerMapper
*
* @var string
*/
- protected static $definitionClass = 'Netzmacht\LeafletPHP\Plugins\OverpassLayer\OverpassLayer';
+ protected static $definitionClass = 'Netzmacht\Contao\Leaflet\Definition\Layer\OverpassLayer';
/**
* {@inheritdoc}
@@ -47,8 +47,8 @@ class OverpassLayerMapper extends AbstractLayerMapper
$this->optionsBuilder
->addOption('query', 'overpassQuery')
- ->addOption('minzoom', 'minZoom')
- ->addOption('debug')
+ ->addOption('minZoom')
+ ->addOption('boundsMode')
->addOption('overpassEndpoint', 'endpoint');
}
@@ -57,7 +57,7 @@ class OverpassLayerMapper extends AbstractLayerMapper
*/
protected function build(
Definition $definition,
- \Model $model,
+ Model $model,
DefinitionMapper $mapper,
Filter $filter = null,
Definition $parent = null
@@ -66,17 +66,12 @@ class OverpassLayerMapper extends AbstractLayerMapper
return;
}
- $minZoomIndicatorOptions = $definition->getMinZoomIndicatorOptions();
- $minZoomIndicatorOptionsBuilder = new OptionsBuilder();
- $minZoomIndicatorOptionsBuilder
- ->addOption('position', 'minZoomIndicatorPosition')
- ->addOption('minZoomMessageNoLayer', 'minZoomIndicatorMessageNoLayer')
- ->addOption('minZoomMessage', 'minZoomIndicatorMessage');
+ if ($model->pointToLayer) {
+ $definition->setPointToLayer(new Expression($model->pointToLayer));
+ }
- $minZoomIndicatorOptionsBuilder->build($minZoomIndicatorOptions, $model);
-
- if ($model->overpassCallback) {
- $definition->setCallback(new Expression($model->overpassCallback));
+ if ($model->onEachFeature) {
+ $definition->setOnEachFeature(new Expression($model->onEachFeature));
}
}
}