Skip to content

Instantly share code, notes, and snippets.

@martykan
Created August 17, 2016 17:15
Show Gist options
  • Save martykan/760576b4344c0c9871adcee2fd3eded5 to your computer and use it in GitHub Desktop.
Save martykan/760576b4344c0c9871adcee2fd3eded5 to your computer and use it in GitHub Desktop.
// Load data tiles from an AJAX data source
L.TileLayer.Ajax = L.TileLayer.extend({
_requests: [],
_addTile: function (tilePoint) {
var tile = { datum: null, processed: false };
this._tiles[tilePoint.x + ':' + tilePoint.y] = tile;
this._loadTile(tile, tilePoint);
},
// XMLHttpRequest handler; closure over the XHR object, the layer, and the tile
_xhrHandler: function (req, layer, tile, tilePoint) {
return function () {
if (req.readyState !== 4) {
return;
}
var s = req.status;
if ((s >= 200 && s < 300) || s === 304) {
tile.datum = JSON.parse(req.responseText);
layer._tileLoaded(tile, tilePoint);
} else {
layer._tileLoaded(tile, tilePoint);
}
};
},
// Load the requested tile via AJAX
_loadTile: function (tile, tilePoint) {
this._adjustTilePoint(tilePoint);
var layer = this;
var req = new XMLHttpRequest();
this._requests.push(req);
req.onreadystatechange = this._xhrHandler(req, layer, tile, tilePoint);
req.open('GET', this.getTileUrl(tilePoint), true);
req.send();
},
_reset: function () {
L.TileLayer.prototype._reset.apply(this, arguments);
for (var i in this._requests) {
this._requests[i].abort();
}
this._requests = [];
},
_update: function () {
if (this._map && this._map._panTransition && this._map._panTransition._inProgress) { return; }
if (this._tilesToLoad < 0) { this._tilesToLoad = 0; }
L.TileLayer.prototype._update.apply(this, arguments);
}
});
L.TileLayer.GeoJSON = L.TileLayer.Ajax.extend({
// Store each GeometryCollection's layer by key, if options.unique function is present
_keyLayers: {},
// Used to calculate svg path string for clip path elements
_clipPathRectangles: {},
initialize: function (url, options, geojsonOptions) {
L.TileLayer.Ajax.prototype.initialize.call(this, url, options);
this.geojsonLayer = new L.GeoJSON(null, geojsonOptions);
},
onAdd: function (map) {
this._map = map;
L.TileLayer.Ajax.prototype.onAdd.call(this, map);
map.addLayer(this.geojsonLayer);
},
onRemove: function (map) {
map.removeLayer(this.geojsonLayer);
L.TileLayer.Ajax.prototype.onRemove.call(this, map);
},
_reset: function () {
this.geojsonLayer.clearLayers();
this._keyLayers = {};
this._removeOldClipPaths();
L.TileLayer.Ajax.prototype._reset.apply(this, arguments);
},
// Remove clip path elements from other earlier zoom levels
_removeOldClipPaths: function () {
for (var clipPathId in this._clipPathRectangles) {
var clipPathZXY = clipPathId.split('_').slice(1);
var zoom = parseInt(clipPathZXY[0], 10);
if (zoom !== this._map.getZoom()) {
var rectangle = this._clipPathRectangles[clipPathId];
this._map.removeLayer(rectangle);
var clipPath = document.getElementById(clipPathId);
if (clipPath !== null) {
clipPath.parentNode.removeChild(clipPath);
}
delete this._clipPathRectangles[clipPathId];
}
}
},
// Recurse LayerGroups and call func() on L.Path layer instances
_recurseLayerUntilPath: function (func, layer) {
if (layer instanceof L.Path) {
func(layer);
}
else if (layer instanceof L.LayerGroup) {
// Recurse each child layer
layer.getLayers().forEach(this._recurseLayerUntilPath.bind(this, func), this);
}
},
_clipLayerToTileBoundary: function (layer, tilePoint) {
// Only perform SVG clipping if the browser is using SVG
if (!L.Path.SVG) { return; }
if (!this._map) { return; }
if (!this._map._pathRoot) {
this._map._pathRoot = L.Path.prototype._createElement('svg');
this._map._panes.overlayPane.appendChild(this._map._pathRoot);
}
var svg = this._map._pathRoot;
// create the defs container if it doesn't exist
var defs = null;
if (svg.getElementsByTagName('defs').length === 0) {
defs = document.createElementNS(L.Path.SVG_NS, 'defs');
svg.insertBefore(defs, svg.firstChild);
}
else {
defs = svg.getElementsByTagName('defs')[0];
}
// Create the clipPath for the tile if it doesn't exist
var clipPathId = 'tileClipPath_' + tilePoint.z + '_' + tilePoint.x + '_' + tilePoint.y;
var clipPath = document.getElementById(clipPathId);
if (clipPath === null) {
clipPath = document.createElementNS(L.Path.SVG_NS, 'clipPath');
clipPath.id = clipPathId;
// Create a hidden L.Rectangle to represent the tile's area
var tileSize = this.options.tileSize,
nwPoint = tilePoint.multiplyBy(tileSize),
sePoint = nwPoint.add([tileSize, tileSize]),
nw = this._map.unproject(nwPoint),
se = this._map.unproject(sePoint);
this._clipPathRectangles[clipPathId] = new L.Rectangle(new L.LatLngBounds([nw, se]), {
opacity: 0,
fillOpacity: 0,
clickable: false,
noClip: true
});
this._map.addLayer(this._clipPathRectangles[clipPathId]);
// Add a clip path element to the SVG defs element
// With a path element that has the hidden rectangle's SVG path string
var path = document.createElementNS(L.Path.SVG_NS, 'path');
var pathString = this._clipPathRectangles[clipPathId].getPathString();
path.setAttribute('d', pathString);
clipPath.appendChild(path);
defs.appendChild(clipPath);
}
// Add the clip-path attribute to reference the id of the tile clipPath
this._recurseLayerUntilPath(function (pathLayer) {
pathLayer._container.setAttribute('clip-path', 'url(#' + clipPathId + ')');
}, layer);
},
// Add a geojson object from a tile to the GeoJSON layer
// * If the options.unique function is specified, merge geometries into GeometryCollections
// grouped by the key returned by options.unique(feature) for each GeoJSON feature
// * If options.clipTiles is set, and the browser is using SVG, perform SVG clipping on each
// tile's GeometryCollection
addTileData: function (geojson, tilePoint) {
var features = L.Util.isArray(geojson) ? geojson : geojson.features,
i, len, feature;
if (features) {
for (i = 0, len = features.length; i < len; i++) {
// Only add this if geometry or geometries are set and not null
feature = features[i];
if (feature.geometries || feature.geometry || feature.features || feature.coordinates) {
this.addTileData(features[i], tilePoint);
}
}
return this;
}
var options = this.geojsonLayer.options;
if (options.filter && !options.filter(geojson)) { return; }
var parentLayer = this.geojsonLayer;
var incomingLayer = null;
if (this.options.unique && typeof(this.options.unique) === 'function') {
var key = this.options.unique(geojson);
// When creating the layer for a unique key,
// Force the geojson to be a geometry collection
if (!(key in this._keyLayers && geojson.geometry.type !== 'GeometryCollection')) {
geojson.geometry = {
type: 'GeometryCollection',
geometries: [geojson.geometry]
};
}
// Transform the geojson into a new Layer
try {
incomingLayer = L.GeoJSON.geometryToLayer(geojson, options.pointToLayer, options.coordsToLatLng);
}
// Ignore GeoJSON objects that could not be parsed
catch (e) {
return this;
}
incomingLayer.feature = L.GeoJSON.asFeature(geojson);
// Add the incoming Layer to existing key's GeometryCollection
if (key in this._keyLayers) {
parentLayer = this._keyLayers[key];
parentLayer.feature.geometry.geometries.push(geojson.geometry);
}
// Convert the incoming GeoJSON feature into a new GeometryCollection layer
else {
this._keyLayers[key] = incomingLayer;
}
}
// Add the incoming geojson feature to the L.GeoJSON Layer
else {
// Transform the geojson into a new layer
try {
incomingLayer = L.GeoJSON.geometryToLayer(geojson, options.pointToLayer, options.coordsToLatLng);
}
// Ignore GeoJSON objects that could not be parsed
catch (e) {
return this;
}
incomingLayer.feature = L.GeoJSON.asFeature(geojson);
}
incomingLayer.defaultOptions = incomingLayer.options;
this.geojsonLayer.resetStyle(incomingLayer);
if (options.onEachFeature) {
options.onEachFeature(geojson, incomingLayer);
}
parentLayer.addLayer(incomingLayer);
// If options.clipTiles is set and the browser is using SVG
// then clip the layer using SVG clipping
if (this.options.clipTiles) {
this._clipLayerToTileBoundary(incomingLayer, tilePoint);
}
return this;
},
_tileLoaded: function (tile, tilePoint) {
L.TileLayer.Ajax.prototype._tileLoaded.apply(this, arguments);
if (tile.datum === null) { return null; }
this.addTileData(tile.datum, tilePoint);
}
});
(function() {
// Define our constructor
this.WeatherLayers = function() {
this.getBackgroundLayer = getBackgroundLayer;
this.getWeatherLayer = getWeatherLayer;
// background layer
function getBackgroundLayer() {
return new L.tileLayer("http://{s}.maps.owm.io/base/gray/{z}/{x}/{y}?appid=2de143494c0b295cca9337e1e96b00e0", {
attribution: "Open Weather Map 2015",
maxZoom: 18,
zIndex: 1
});
}
// weather layer
function getWeatherLayer() {
var getTempClass = function(t) {
var c = 'weather-none';
if(t > - 20 && t <= 6) c = 'weather-cold';
if(t <= 2) c = 'weather-very-cold';
if(t > 6 && t <= 20) c = 'weather-average';
if(t > 20 && t <=28) c = 'weather-hot';
if(t > 28) c = 'weather-very-hot';
return c;
};
return new L.TileLayer.GeoJSON('http://{s}.maps.owm.io/weather/cities/{z}/{x}/{y}.geojson?appid=2de143494c0b295cca9337e1e96b00e0', {
clipTiles: true
},
{
pointToLayer: function (feature, latlng) {
var cityName = feature.properties['city'].replace(" ", " ");
var temp = Math.floor(feature.properties.temp);
var icon = L.divIcon({
className: 'marker-default',
html: [
'<div>',
'<span class="city-bullet">•</span>',
'<div class="city-data">',
'<div class="row city-main-info">',
'<span class="city-weather">' + temp + ' </span>',
'<span class="city-name ' + getTempClass(temp) +'">' + cityName + '</span>',
'</div>',
'<div class="row city-full-info">',
'<table>',
'<thead>',
'<tr>',
'<th colspan=2 class="city-param ' + getTempClass(temp) +'">' + cityName + '</td>',
'</tr>',
'</thead>',
'<tbody>',
'<tr>',
'<td class="city-param-name">country</td>',
'<td class="city-param">' + feature.properties.country + '</td>',
'</tr>',
'<tr>',
'<td class="city-param-name">temp</td>',
'<td class="city-param">' + feature.properties.temp + '</td>',
'</tr>',
'<tr>',
'<td class="city-param-name">clouds</td>',
'<td class="city-param">' + feature.properties.clouds + '</td>',
'</tr>',
'<tr>',
'<td class="city-param-name">humidity</td>',
'<td class="city-param">' + feature.properties.humidity + '</td>',
'</tr>',
'<tr>',
'<td class="city-param-name">pressure</td>',
'<td class="city-param">' + feature.properties.pressure + '</td>',
'</tr>',
'<tr>',
'<td class="city-param-name">wind_deg</td>',
'<td class="city-param">' + feature.properties.wind_deg + '</td>',
'</tr>',
'<tr>',
'<td class="city-param-name">wind_speed</td>',
'<td class="city-param">' + feature.properties.wind_speed + '</td>',
'</tr>',
'</tbody>',
'</table>',
'</div>',
'</div>',
'</div>'].join('')
});
return L.marker(latlng, {
icon: icon,
riseOnHover: true,
clickable: true
});
},
onEachFeature: function (feature, layer) {
if (!(layer instanceof L.Point)) {
layer.on('click', function (e) {
var classes = e.target._icon.className;
if (classes.indexOf('expanded') + 1) {
e.target._icon.className = e.target._icon.className.replace("expanded", "");
e.target._zIndex -= e.target.options.riseOffset;
} else {
e.target._icon.className += ' expanded';
e.target._zIndex += e.target.options.riseOffset;
}
});
}
}
});
}
};
}());
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment