/**
* Class constructor to draw Caercalia Map POIS.Para hacer una descarga de pois que no sea de Cercalia vease el <a href="#">ejemplo</a>.
* @class
* @constructor
* @param {cercaliax.TilePoiMapOptions} options TilePoiMap options'
*/
cercalia.TilePoiMap = function(options) {
/**
* Class name
* @private
* @type {string}
*/
this.CLASS_NAME_ = "cercalia.TilePoiMap";
/* OPTIONS */
/**
* Tile size. Default [256, 256].
* @private
* @type {Array.<number>}
*/
this.tileSize_ = options.tileSize ? options.tileSize : [256, 256];
/**
* Zoom level that the size of the icons change. Only it used in the default download version.
* @private
* @type {number}
*/
this.iconBigZoomLevel_ = options.iconBigZoomLevel ? options.iconBigZoomLevel : 15;
/**
* Custom function to create markers. This function must return an array of markers
* @private
* @type {Function}
*/
this.createMarkersFunction_ = options.createMarkersFunction ? options.createMarkersFunction : null;
/**
* Map where markers are painted
* @private
* @type {cercalia.Map}
*/
this.map_ = options.map ? options.map : null;
/* VARIABLES PRIVADAS */
/**
* List of Cercalia categories to paint on map.
* @private
* @type {Array.<string>}
*/
this.categoriesList_ = [];
/**
* Layer simulating downloading tiles.
* @private
* @type {ol.layer.Vector}
*/
this.vectorLayer_ = null;
/**
* Hash (key => zoom) painted markers.
* @private
* @type {Object}
*/
this.markersPainted_ = {};
/**
* Hash (key => ZXY) ajax requests.
* @private
* @type {Object}
*/
this.xhrPoisHash_ = {};
/**
* Tiles grid.
* @private
* @type {ol.tilegrid.XYZ}
*/
this.tileGrid_ = null;
/**
* Current zoom level.
* @private
* @type {number}
*/
this.zoomMap_ = null;
this.timeoutLoadPois_ = null;
/* SERVICIOS Y WIDGETS DE CERCALIA */
/**
* Get POIs service
* @private
* @type {cercaliax.service.Pois}
*/
this.servicePoisOptions_ = {
searchByScale : true,
height: this.tileSize_,
width: this.tileSize_//,
//gridsize: 5000
};
// Inicialitze
this.initialize_();
};
/**
* Initializes the layer Source definition and create the function to be called for each of the downloaded tiles.
* @private
*/
cercalia.TilePoiMap.prototype.initialize_ = function() {
if(this.map_ != null) {
// Si existe mapa añadimos el layer.
this.vectorLayer_ = new ol.layer.Vector({ source: this.initSource_() });
this.map_.addLayer(this.vectorLayer_);
this.map_.getMap().on('moveend', this.moveEndFunction_, this);
} else {
console.error("No map defined.");
}
};
/**
* Initializes the layer Source definition and create the function to be called for each of the downloaded tiles.
* @private
*/
cercalia.TilePoiMap.prototype.initSource_ = function() {
var self = this;
var vectorSource = null;
var viewOptions = this.map_.getViewOptions();
// Creamos el tile grid.
this.tileGrid_ = this.createTileGrid_({
/*tileSize: this.tileSize_/*,
maxZoom: viewOptions.maxZoom,
minZoom: viewOptions.minZoom/*,
extent: [-20037508.34, -20037508.34, 20037508.34, 20037508.34]*/
});
// Inicializamos el source del layer. Se encarga de descargar y pintar los pois en el mapa.
var sourceParams = {
//format: new ol.format.GeoJSON(),
loader: this.loader_(),
projection: this.map_.getProjectionCode(),
strategy: this.strategy_(this.tileGrid_),
tileGrid: this.tileGrid_
};
// Creación del source
vectorSource = new cercalia.tile.ServerVector(sourceParams);
return vectorSource;
};
/**
* Returns the strategy function followed to download pois.
* @private
* @param {ol.tilegrid.XYZ} tileGrid Map tiles grid.
* @returns {Function}
*/
cercalia.TilePoiMap.prototype.strategy_ = function(tileGrid) {
var self = this;
/**
* @param {ol.Extent} extent Extent.
* @param {number} resolution Resolution.
* @return {Array.<ol.Extent>} Extents.
*/
var startegy = function(extent, resolution) {
var extents = [];
// console.group("Strategy");
// Si no hay categorias a pintar no devolvemos tiles.
if(self.categoriesList_ && self.categoriesList_.length > 0) {
// Obtenemos zoom y rango de tiles a pintar
var z = tileGrid.getZForResolution(resolution);
var tileRange = tileGrid.getTileRangeForExtentAndZ(extent, z);
var tileCoord = [z, 0, 0];
// console.log("Calculate range: ", tileRange);
// Guardamos la extensión y el id de la tile.
for (tileCoord[1] = tileRange.minX; tileCoord[1] <= tileRange.maxX; ++tileCoord[1]) {
for (tileCoord[2] = tileRange.minY; tileCoord[2] <= tileRange.maxY; ++tileCoord[2]) {
extents.push({
extent: tileGrid.getTileCoordExtent(tileCoord),
tileId: [tileCoord[0], tileCoord[1], tileCoord[2]]
});
}
}
}
// console.log("Extents to paint: ", extents);
// console.groupEnd();
return extents;
};
return startegy;
};
/**
* Function for painting downloaded markers.
* @private
* @return {Function}
*/
cercalia.TilePoiMap.prototype.loader_ = function() {
var self = this;
/**
* Function for painting downloaded markers.
* @private
* @param {ol.Extent} extent Tile extent
* @param {number} resolution Tile resolution
* @param {string} projection Map projection.
* @param {Object} tileId Tile ZXY id.
*/
var loaderFuntion = function (extent, resolution, projection, tileId) {
// Calculamos BBOX i zoom del tile.
var ws = new cercalia.LonLat(extent[0], extent[1], projection.getCode());
var en = new cercalia.LonLat(extent[2], extent[3], projection.getCode());
//var zoomLevel = this.map_.getZoom();
var zoomLevel = self.tileGrid_.getZForResolution(resolution);
var bounds = new cercalia.Bounds([ws.getLon(), ws.getLat(), en.getLon(), en.getLat()]);
// Inicializamos variables contenedores de Ids y markers
if(!self.markersPainted_[zoomLevel]) self.markersPainted_[zoomLevel] = [];
// Descargamos!
if(self.checkZoomWidthMapZoom_(zoomLevel)) {
// console.log("Tile " + tileId.join(", ") + ": download.");
self.dowloadFunction_(bounds, self.categoriesList_, zoomLevel, function(list) { self.paintMarkersFuncion_(list, zoomLevel, tileId); });
} else {
// console.log("Tile " + tileId.join(", ") + ": descartada, " + zoomLevel);
// Marcamos la tile como descartada
self.vectorLayer_.getSource().setLoadedTile(tileId, false);
// Cancelamos peticiones pendientes de ese nivel de zoom
cercalia.jQuery(self.xhrPoisHash_[zoomLevel]).each(function (index, element) {
element.abortLast();
});
self.xhrPoisHash_[zoomLevel] = [];
}
};
return loaderFuntion;
};
/**
* Checks whether the zoom is like the map
* @private
* @param {number} zoom Zoom level to check
* @returns {Boolean} True if zoom is == map zoom.
*/
cercalia.TilePoiMap.prototype.checkZoomWidthMapZoom_ = function(zoom) {
var resolution = this.map_.getMap().getView().getResolution();
var mapZoomLevel = this.tileGrid_.getZForResolution(resolution);
return mapZoomLevel == zoom;
};
/**
* POIs download function.
* @private
* @param {cercalia.Bounds} bounds Tile BBOX.
* @param {Array.<string>} categoriesList Categories list.
* @param {number} zoomLevel Tile zoom level.
* @param {Function} paintFunction Paint POIs function.
*/
cercalia.TilePoiMap.prototype.dowloadFunction_ = function(bounds, categoriesList, zoomLevel, paintFunction) {
// Creamos servicio para obtener los pois por tiles.
var servicePois = new cercalia.service.Pois(this.servicePoisOptions_);
servicePois.setBounds(bounds);
servicePois.setQueryPois(categoriesList);
// Inicializamos hash del zoom de XHR si no lo esta.
if(!this.xhrPoisHash_[zoomLevel]) this.xhrPoisHash_[zoomLevel] = [];
this.xhrPoisHash_[zoomLevel].push(servicePois);
// Hacemos el request de los pois
var self = this;
// El usuario puede especificar una funcion personalizada.
var createMarkersFn = this.createMarkersFunction_ ? this.createMarkersFunction_ : this.createCercaliaMarkers_;
servicePois.doPoisRequest(function (response) {
// Generamos los markers
var jsonMarkersArray = response.cercalia.map.gpoicats.poilist.poi instanceof Array ? response.cercalia.map.gpoicats.poilist.poi : [];
var markersArray = createMarkersFn(jsonMarkersArray, bounds, zoomLevel);
// console.log("Pois: ", bounds, zoomLevel, markersArray);
// Pintamos
paintFunction(markersArray, zoomLevel);
});
};
/**
* Paint markers on the map and saves the hash markers.
* @private
* @param {Array.<cercalia.Marker>} markersArray
* @param {number} zoomLevel
* @param {Object} tileId ZXY tile ID.
*/
cercalia.TilePoiMap.prototype.paintMarkersFuncion_ = function(markersArray, zoomLevel, tileId) {
if(!(markersArray instanceof Array)) markersArray = [];
// Pintamos los markers
if(this.checkZoomWidthMapZoom_(zoomLevel)) {
this.map_.addApiMarkers(markersArray);
this.markersPainted_[zoomLevel] = this.markersPainted_[zoomLevel].concat(markersArray);
} else {
// Marcamos la tile como descartada
this.vectorLayer_.getSource().setLoadedTile(tileId, false);
// Cancelamos peticiones pendientes de ese nivel de zoom
cercalia.jQuery(this.xhrPoisHash_[zoomLevel]).each(function (index, element) {
element.abortLast();
});
this.xhrPoisHash_[zoomLevel] = [];
}
};
/**
* Create a list of markers to paint from server json, a BBOX and zoom level.
* @private
* @param {Object} json Json downloaded from server.
* @param {cercalia.Bounds} bounds BBOX (bounds).
* @param {number} zoomLevel Zoom level.
*/
cercalia.TilePoiMap.prototype.createCercaliaMarkers_ = function(json, bounds, zoomLevel) {
var listMarkers = [];
var self = this;
json.forEach(function(elem, index) {
var markerLonLat = new cercalia.LonLat(elem.coord.x, elem.coord.y);
if(bounds.containsLonLat(markerLonLat)) {
// "subcategory_id":"SEC",
// és una prova
// el problema estava en els pois: D00ESC
// Els pois que tenen subcategoria no existeix la img de {category_id}.png
var imgPoiName = elem.category_id;
if( elem.subcategory_id !== undefined
&& elem.subcategory_id != null
&& elem.subcategory_id != "-1" ){
imgPoiName += "_" + elem.subcategory_id;
}
var img = cercaliaGlobals.img + '/pois/' + (zoomLevel >= self.iconBigZoomLevel_ ? 'big' : 'small') + '/' + imgPoiName + '.png';
var marker = new cercalia.Marker({
id: elem.id ? elem.id : null,
position : markerLonLat,
icon : new cercalia.Icon({src:img}),
popup : new cercalia.Popup({
title: elem.name.value,
content: "<div>" + elem.info.value + "</div>"
}),
onClick: function() {
if(!this.getPopup().isOpen()) this.getPopup().show();
else this.getPopup().hide();
}
});
listMarkers.push(marker);
}
});
return listMarkers;
};
/**
* Call function on map moveend.
* @private
* @param {ol.Event} event Event.
*/
cercalia.TilePoiMap.prototype.moveEndFunction_ = function(event) {
var resolution = this.map_.getMap().getView().getResolution();
var zoomLevel = this.tileGrid_.getZForResolution(resolution);
if(zoomLevel != this.zoomMap_) {
// console.log(zoomLevel + " != " + this.zoomMap_);
this.zoomMap_ = zoomLevel;
this.clearAllExcept(this.zoomMap_);
}
// Recargamos las tiles de la extension actual del mapa.
if(this.timeoutLoadPois_ != null) {
window.clearTimeout(this.timeoutLoadPois_);
this.timeoutLoadPois_ = null;
}
var olMap = this.map_.getMap();
var mapSize = olMap.getSize();
var mapExtent = olMap.getView().calculateExtent(mapSize);
this.vectorLayer_.getSource().loadFeatures(mapExtent, olMap.getView().getResolutionForExtent(mapExtent, mapSize), olMap.getView().getProjection());
};
/**
* Remove all markers from a specific zoom level.
* @private
* @param {number} zoomLevel Zoom level to clean.
*/
cercalia.TilePoiMap.prototype.clear_ = function(zoomLevel) {
if(zoomLevel) {
// Borramos markers del nivel seleccionado.
if(this.markersPainted_[zoomLevel] && this.markersPainted_[zoomLevel].length > 0) {
this.map_.removeApiMarkers(this.markersPainted_[zoomLevel]);
}
this.markersPainted_[zoomLevel] = [];
// Marcamos las tiles de ese nivel de zoom como no cargadas.
this.vectorLayer_.getSource().clear(zoomLevel);
// Cancelamos peticiones activas de este nivel de zoom y las eliminamos
cercalia.jQuery(this.xhrPoisHash_[zoomLevel]).each(function (index, element) {
element.abortLast();
});
this.xhrPoisHash_[zoomLevel] = [];
// console.group("Clear");
// console.log("zoomLevel = " + zoomLevel);
// console.log("markersPainted_ = ", this.markersPainted_);
// console.log("tileLoadedHash_ = ", this.tileLoadedHash_);
// console.groupEnd();
}
};
/**
* Remove all markers except a specified zoom.
* @param {number} zoomLevel Zoom level to mantain (not clean).
*/
cercalia.TilePoiMap.prototype.clearAllExcept = function(zoomLevel) {
if(zoomLevel) {
for(var index in this.markersPainted_) {
if(zoomLevel != index) this.clear_(index);
}
}
};
/**
* Remove painted markers, for all zoom levels.
*/
cercalia.TilePoiMap.prototype.clearAll = function() {
for(var index in this.markersPainted_) {
this.clear_(index);
}
this.markersPainted_ = {};
};
/**
* Removes the map layer.
*/
cercalia.TilePoiMap.prototype.remove = function() {
this.clearAll();
this.map_.removeLayer(this.vectorLayer_);
};
/**
* Modify the Cercalia's categories list to paint on map.
* @param {Array.<string>} categories Categories list to paint.
*/
cercalia.TilePoiMap.prototype.setCategories = function(categories) {
this.categoriesList_ = categories;
//console.log("Set categories to [" + this.categoriesList_.join(", ") + "]");
if(this.map_) {
// Quitamos todos los marcadores.
this.clearAll();
// Recargamos las tiles de la extension actual del mapa.
var olMap = this.map_.getMap();
var mapSize = olMap.getSize();
var mapExtent = olMap.getView().calculateExtent(mapSize);
this.vectorLayer_.getSource().loadFeatures(mapExtent, olMap.getView().getResolutionForExtent(mapExtent, mapSize), olMap.getView().getProjection());
}
};
/**
* Create a ol.tilegrid.XYZ object and adds functions.
* @param {Object} options ol.tilegrid.XYZ initialization options
* @private
*/
cercalia.TilePoiMap.prototype.createTileGrid_ = function (options) {
var tilegrid = new ol.tilegrid.createXYZ(options);
/**
* Returns the zoom level according to the resolution.
* @param {number} resolution Resolution.
* @return {Number}
*/
tilegrid.getZForResolution = function (resolution) {
var resolutions = this.getResolutions();
var n = resolutions.length;
for (i = 1; i < n; ++i) {
if (resolutions[i] == resolution) {
return i;
} else if (resolutions[i] < resolution) {
if (resolutions[i - 1] - resolution < resolution - resolutions[i]) {
return i - 1;
} else {
return i;
}
}
}
return 0;
};
/**
* @param {number} x X.
* @param {number} y Y.
* @param {number} resolution Resolution.
* @param {Boolean} reverseIntersectionPolicy Instead of letting edge intersections go to the higher tile coordinate, let edge intersections go to the lower tile coordinate.
* @return {ol.TileCoord} tile coordinate.
*/
tilegrid.getTileCoordForXYAndResolution = function (x, y, resolution, reverseIntersectionPolicy) {
var z = this.getZForResolution(resolution);
var scale = resolution / this.getResolution(z);
var origin = this.getOrigin(z);
var tileSize = this.getTileSize(z);
var tileCoordX = scale * (x - origin[0]) / (resolution * tileSize);
var tileCoordY = scale * (y - origin[1]) / (resolution * tileSize);
if (reverseIntersectionPolicy) {
tileCoordX = Math.ceil(tileCoordX) - 1;
tileCoordY = Math.ceil(tileCoordY) - 1;
} else {
tileCoordX = Math.floor(tileCoordX);
tileCoordY = Math.floor(tileCoordY);
}
return [z, tileCoordX, tileCoordY];
};
/**
* @param {ol.Extent} extent Extent.
* @param {number} resolution Resolution.
* @return {Object} Tile range.
*/
tilegrid.getTileRangeForExtentAndResolution = function (extent, resolution) {
var min = this.getTileCoordForXYAndResolution(extent[0], extent[1], resolution, false);
var max = this.getTileCoordForXYAndResolution(extent[2], extent[3], resolution, true);
return {
minX: min[1],
maxX: max[1],
minY: min[2],
maxY: max[2]
};
};
/**
* @param {ol.Extent} extent Extent.
* @param {number} z Zoom level.
* @return {Object} Tile range.
*/
tilegrid.getTileRangeForExtentAndZ = function (extent, z) {
var resolution = this.getResolution(z);
return this.getTileRangeForExtentAndResolution(extent, resolution);
};
/**
* @param {ol.TileCoord} tileCoord Tile coordinate.
* @return {ol.Extent} Extent.
*/
tilegrid.getTileCoordExtent = function(tileCoord) {
var origin = this.getOrigin(tileCoord[0]);
var resolution = this.getResolution(tileCoord[0]);
var tileSize = this.getTileSize(tileCoord[0]);
var minX = origin[0] + tileCoord[1] * tileSize * resolution;
var minY = origin[1] + tileCoord[2] * tileSize * resolution;
var maxX = minX + tileSize * resolution;
var maxY = minY + tileSize * resolution;
return [minX, minY, maxX, maxY];
};
return tilegrid;
};