/* default variables */
var default_pointer = "/media/images/default-pointer.png";
var marker_cluster = "/media/images/marker-cluster.png";
var view_projection = 'EPSG:3857';
var animation_duration = 250;
var cluster_threshold_1 = 8;
var cluster_threshold_2 = 25;
var base_color_R = 111;
var base_color_V = 66;
var base_color_B = 193;
var base_color_rvb;
var map_default_center = 'SRID=4326;POINT (2.4397 46.5528)';
var map_default_zoom = '7';
var min_auto_zoom_on_cluster = 13;
/* custom control */
var track_me_msg = "Geolocalize me";
var geoloc_activated_msg = "Geolocation activated";
var geoloc_disabled_msg = "Geolocation disabled";
var geolocation = {};
var geoloc_feature = {};
var geoloc_activated = {};
var fetching_msg = "Fetching data...";
var _map_submit_search = function(query_vars, name, source){
var modal_base_text = $('.modal-progress .modal-header').html();
$('.modal-progress .modal-header').html(fetching_msg);
$('.modal-progress').modal('show');
var data = search_get_query_data(query_vars, name);
var nb_select = jQuery("#id_" + name + "-length_map").val();
if (!nb_select) nb_select = 10;
var url = source + "json-map?length=" + nb_select + "&submited=1&" + data;
var use_map_limit = false;
if(data.indexOf("no_limit=true") == -1){
url += "&limit=" + current_map_limit;
use_map_limit = true;
}
$.getJSON(url, function(data) {
var timestamp = Math.floor(Date.now() / 1000);
var map_id = "map-" + timestamp;
$('.modal-progress .modal-header').html("{% trans 'Render map...' %}");
var html = render_map(map_id, use_map_limit);
$("#tab-content-map-" + name).html(html);
$("#id_" + name + "-length_map").change(map_submit_search);
if ($('.modal-progress').length > 0){
$('.modal-progress').modal('hide');
$('.modal-progress .modal-header').html(modal_base_text);
}
register_map(map_id, data);
});
return false;
};
var geoloc_activated_message = function(map_id){
setTimeout(function(){
navigator.geolocation.watchPosition(function(position) {
if(!geoloc_activated[map_id]){
if (display_info) display_info(geoloc_activated_msg);
geoloc_activated[map_id] = true;
}
},
function (error) {
if (error.code == error.PERMISSION_DENIED)
if (display_info) display_info(geoloc_disabled_msg);
});
}, 200);
};
var set_geoloc_source = function(map_id){
geolocation[map_id] = new ol.Geolocation({
projection: view_projection
});
geolocation[map_id].setTracking(true);
geoloc_feature[map_id] = new ol.Feature();
geoloc_feature[map_id].setStyle(new ol.style.Style({
image: new ol.style.Circle({
radius: 6,
fill: new ol.style.Fill({color: '#3399CC'}),
stroke: new ol.style.Stroke({color: '#fff', width: 2})
})
}));
var accuracy_feature = new ol.Feature();
geolocation[map_id].on('change:accuracyGeometry', function() {
accuracy_feature.setGeometry(geolocation[map_id].getAccuracyGeometry());
});
geolocation[map_id].on('change:position', function() {
var coordinates = geolocation[map_id].getPosition();
geoloc_feature[map_id].setGeometry(
coordinates ? new ol.geom.Point(coordinates) : null);
var v = map[map_id].getView();
v.animate({center: coordinates, duration: animation_duration * 2});
});
new ol.layer.Vector({
map: map[map_id],
source: new ol.source.Vector({
features: [geoloc_feature[map_id], accuracy_feature]
})
});
geoloc_activated_message(map_id);
};
var TrackPositionControl = (function (Control) {
function TrackPositionControl(opt_options) {
var options = opt_options || {};
this.map_id = options['map_id'];
var button = document.createElement('button');
button.type = "button";
button.innerHTML = '';
button.title = track_me_msg;
var element = document.createElement('div');
element.className = 'track-position ol-unselectable ol-control';
element.appendChild(button);
Control.call(this, {
element: element,
target: options.target
});
button.addEventListener(
'click', this.handleTrackPosition.bind(this),
false
);
}
if ( Control ) TrackPositionControl.__proto__ = Control;
TrackPositionControl.prototype = Object.create( Control && Control.prototype );
TrackPositionControl.prototype.varructor = TrackPositionControl;
TrackPositionControl.prototype.handvarrackPosition = function handvarrackPosition () {
if (!geolocation[this.map_id]){
set_geoloc_source(this.map_id);
} else {
if (!geoloc_activated[this.map_id]) return;
if (geolocation[this.map_id].getTracking()){
geolocation[this.map_id].setTracking(false);
if (display_info) display_info(geoloc_disabled_msg);
} else {
geolocation[this.map_id].setTracking(true);
if (display_info) display_info(geoloc_activated_msg);
}
}
return false;
};
return TrackPositionControl;
}(ol.control.Control));
/* base layers */
var source_osm = function(options){
return new ol.layer.Tile({
source: new ol.source.OSM()
});
};
var default_map_layers = {
'osm': source_osm
};
var get_layers = function(layers){
if (!layers){
layers = [{'type': 'osm', 'options': null}];
}
var ol_layers = [];
for (idx in layers){
var layer_attr = layers[idx];
ol_layers.push(
default_map_layers[layer_attr['type']](layer_attr['options'])
);
}
return ol_layers;
};
/* styles */
var get_icon_style = function(feature){
return new ol.style.Style({
image: new ol.style.Icon({
anchor: [17, 50],
anchorXUnits: 'pixels',
anchorYUnits: 'pixels',
size: [35, 50],
src: static_path + default_pointer
})
});
};
var get_vector_style = function(feature){
return new ol.style.Style({
stroke: new ol.style.Stroke({
color: 'rgba(' + base_color_rvb + ', 1)',
width: 2
}),
fill: new ol.style.Fill({
color: 'rgba(' + base_color_rvb + ', 0.2)'
})
});
};
var cluster_get_style = function(feature, resolution){
feature.set('key', 'cluster');
var cluster_features = feature.get('features');
var size = cluster_features.length;
feature.set('size', size);
var style = _styleCache[size];
if (!style && size == 1){
style = _styleCache[size] = [get_icon_style()];
} else if (!style && size > 1){
var color = size > cluster_threshold_2 ? "192,0,0" : size > cluster_threshold_1 ? "255,128,0" : "0,128,0";
var radius = Math.max(8, Math.min(size * 0.75, 20));
var lbl = size.toString();
style = _styleCache[size] = [
new ol.style.Style({
image: new ol.style.Circle({
radius: radius,
stroke: new ol.style.Stroke({
color:"rgba("+color+",0.5)",
width: 15
}),
fill: new ol.style.Fill({
color:"rgba("+color+",1)"
})
}),
text: new ol.style.Text({
text: lbl,
fill: new ol.style.Fill({
color: '#fff'
})
})
})
];
}
return style;
}
/* clustering */
var _styleCache;
var cluster_source = {};
var cluster_layer = {};
var enable_clustering = function(map_id){
// cache for styles
_styleCache = {};
// cluster Source
cluster_source[map_id] = new ol.source.Cluster({
distance: 40,
source: new ol.source.Vector()
});
// animated cluster layer
cluster_layer[map_id] = new ol.layer.Vector({
name: 'Cluster',
source: cluster_source[map_id],
// cluster style
style: cluster_get_style
});
map[map_id].addLayer(cluster_layer[map_id]);
};
var reinit_clustering = function(map_id){
if (map_id in cluster_source) {
cluster_source[map_id].getSource().clear();
}
_styleCache = {};
};
/* manage clicks */
var current_feature;
var animate_in_progress = false;
var animate_end = function(){animate_in_progress = false};
var wait_animation_end = function(callback, retry){
if (!retry) retry = 1;
setTimeout(function(){
retry += 1
if (retry < 5 && animate_in_progress){
wait_animation_end(callback)
} else {
callback();
}
}, 100);
};
var manage_click_on_map = function(map_id){
return function(e) {
var feature = map[map_id].forEachFeatureAtPixel(
e.pixel,
function(feature, layer) {
return feature;
}
);
click_on_feature(map_id, feature, e);
};
};
var current_event;
var click_on_feature = function(map_id, feature, e){
// console.log("click_on_feature");
current_event = e;
if (!$(e.target).is($(popup_item[map_id]))
&& !$.contains($(popup_item[map_id])[0], e.target) ) {
$(popup_item[map_id]).hide();
$('#ishtar-map-window-' + map_id).hide();
}
if (typeof feature == 'undefined'){
current_feature = null;
return;
}
current_feature = feature;
if (!feature) return;
var timeout = 10;
setTimeout(function(){
// zoom on aggregated
var key = feature.get('key');
if (feature.get('name') || feature.get('key')){
feature = click_on_cluster(map_id, feature);
}
}, timeout);
};
var auto_zoom = false;
var click_on_cluster = function(map_id, feature, zoom_level, duration, nb_zoom,
current_nb_items){
// console.log("click_on_cluster");
if (!duration){
// zoom animation must be slower
duration = animation_duration * 2;
}
if (!nb_zoom) nb_zoom = 0;
var props = feature.getProperties();
if (!'features' in props) return feature;
if (!auto_zoom || props['features'].length == 1){
return display_cluster_detail(map_id, feature);
}
if (!current_nb_items){
current_nb_items = props['features'].length;
} else if(current_nb_items != props['features'].length) {
// stop zooming there less item in the cluster
return feature;
}
var v = map[map_id].getView();
if (!zoom_level) zoom_level = v.getZoom() + 1;
// center
var new_center = feature.getGeometry().getCoordinates();
// max zoom reached
if (zoom_level >= min_auto_zoom_on_cluster){
animate_in_progress = true;
v.animate({center: new_center, duration: duration}, animate_end);
return display_cluster_detail(map_id, feature);
}
// zoom
animate_in_progress = true;
v.animate({center: new_center, zoom: zoom_level, duration: duration},
animate_end);
nb_zoom += 1;
// something wrong stop zoom!
if (nb_zoom > v.getMaxZoom()) return feature;
// wait for the animation to finish before rezoom
return setTimeout(
function(){
// our cluster must be at the center (if it exists after zoom)
var pixel = map[map_id].getPixelFromCoordinate(v.getCenter());
var new_feature;
map.forEachFeatureAtPixel(
pixel, function(feat, layer){
if (layer == cluster_layer[map_id]){
new_feature = feat;
return true
}
}
);
if (new_feature){
if (zoom_level < min_auto_zoom_on_cluster){
return display_cluster_detail(map_id, new_feature);
}
return click_on_cluster(
map_id, new_feature, zoom_level + 1, duration, nb_zoom,
current_nb_items);
}
// no more cluster feature here or min auto zoom reach: stop zooming
return feature;
}, duration + 200);
};
/* display info */
var display_cluster_detail = function(map_id, cluster){
// console.log("display_cluster_detail");
var features = cluster.getProperties()['features']
var offset_x = 0;
var offset_y = -21;
if (!features){
features = [cluster];
offset_y = -7;
} else if (features.length == 1){
offset_y = -54;
}
display_items(map_id, features, offset_x, offset_y);
};
var display_items = function(map_id, features, offset_x, offset_y){
wait_animation_end(function() {_display_items(map_id, features, offset_x, offset_y)});
};
var open_map_window = function(map_id){
return function(){
$('#ishtar-map-window-' + map_id).show();
};
};
var complete_list_label = "compvare list...";
var _display_items = function(map_id, features, offset_x, offset_y){
// console.log("display_items");
var feature = features[0];
var geom = feature.getGeometry();
var ul_class = "map-list-" + map_id;
var popup_content = "
";
var window_content = "";
var has_extra = false;
for (idx_feat in features){
if (idx_feat == 5){
popup_content += "- ";
popup_content += complete_list_label;
popup_content += "
";
}
var feat = features[idx_feat];
var properties = feat.getProperties();
var link = link_template[map_id].replace("", properties["id"]);
var txt = "- " + link + " " + properties['name'] + "
";
window_content += txt;
if (idx_feat < 5){
popup_content += txt;
}
}
popup_content += "
";
window_content += "
";
$("#ishtar-map-window-" + map_id + " .modal-body").html(window_content);
$(popup_item[map_id]).html(popup_content);
$("." + ul_class + " .map-list-detail").click(open_map_window(map_id));
if (geom.getType() == "Point"){
popup[map_id].setPosition(geom.getCoordinates());
} else {
popup[map_id].setPosition(current_event.coordinate);
}
popup[map_id].setOffset([offset_x, offset_y]);
$(popup_item[map_id]).css({opacity: 0});
$(popup_item[map_id]).show(0, function(){
setTimeout(function(){
popup[map_id].setOffset([offset_x, offset_y]);
$(popup_item[map_id]).css({opacity: 1});
}, 200);
});
};
/* hover */
var manage_hover = function(map_id){
return function(e) {
var pixel = map[map_id].getEventPixel(e.originalEvent);
var feature = map[map_id].forEachFeatureAtPixel(
e.pixel,
function(feature, layer) {
return feature;
}
);
var hit = map[map_id].hasFeatureAtPixel(pixel);
var target = map[map_id].getTarget();
target = typeof target === "string" ?
document.getElementById(target) : target;
target.style.cursor = hit ? 'pointer' : '';
}
};
/* popup */
var popup = {};
var popup_item = {};
var init_popup = function(map_id){
popup_item[map_id] = document.getElementById("ishtar-map-popup-" + map_id);
var popup_options = {
element: popup_item[map_id],
positioning: 'bottom-center'
}
popup[map_id] = new ol.Overlay(popup_options);
map[map_id].addOverlay(popup[map_id]);
};
/* display map */
var center;
var point_features = {};
var map = {};
var map_view = {};
var map_layers = {};
var proj_options = {
dataProjection:'EPSG:4326', featureProjection: view_projection
}
var geojson_format = new ol.format.GeoJSON(proj_options);
var wkt_format = new ol.format.WKT(proj_options);
var link_template = {};
var vector_source = {};
var vector_layer = {};
var vector_features = {};
// for test
var geo_items_features = null;
var current_test = false;
var initialize_test_map = function (slug_pk) {
var id = "http-geo-items-ready-" + slug_pk;
geo_items_features = {};
current_test = true;
if ($("#"+id).length === 0) {
$("#display-geo-items").after('Ready!
');
}
$("#"+id).hide();
}
var initialize_base_map = function(map_id, layers){
center = wkt_format.readGeometry(map_default_center).getCoordinates();
map_layers[map_id] = get_layers(layers);
map_view[map_id] = new ol.View({
projection: view_projection,
center: ol.proj.fromLonLat([center[0], center[1]]),
zoom: map_default_zoom
});
var map_controls = ol.control.defaults().extend([
new ol.control.OverviewMap({
layers: map_layers[map_id]
}),
new ol.control.FullScreen(),
new ol.control.ScaleLine()
]);
if (location.protocol == 'https:'){
map_controls.push(
new TrackPositionControl({map_id: map_id})
);
}
map[map_id] = new ol.Map({
controls: map_controls,
target: map_id,
layers: map_layers[map_id],
view: map_view[map_id]
});
}
var redraw_map = function(map_id, layers){
map[map_id].setTarget(null);
map[map_id] = null;
initialize_base_map(map_id, layers);
reinit_clustering(map_id);
current_feature = null;
};
var display_map = function(map_id, points, lines_and_polys, layers){
base_color_rvb = base_color_R + ', ' + base_color_V + ', ' + base_color_B;
if (points){
link_template[map_id] = points['link_template'];
} else {
link_template[map_id] = lines_and_polys['link_template'];
}
if (map[map_id]){
redraw_map(map_id, layers);
} else {
initialize_base_map(map_id, layers);
}
display_points(map_id, points);
display_lines_and_polys(map_id, lines_and_polys);
init_popup(map_id);
map[map_id].on('click', manage_click_on_map(map_id));
map[map_id].on('pointermove', manage_hover(map_id));
if (current_test) {
geo_items_features[map_id] = []; // for test
}
};
var display_points = function(map_id, points){
if (!points) return;
point_features[map_id] = geojson_format.readFeatures(points);
enable_clustering(map_id);
cluster_source[map_id].getSource().addFeatures(point_features[map_id]);
if (points.features.length){
map_view[map_id].fit(cluster_source[map_id].getSource().getExtent());
if (map_view[map_id].getZoom() > 12){
map_view[map_id].setZoom(12);
}
}
};
var display_lines_and_polys = function(map_id, lines_and_polys){
if (!lines_and_polys) return;
vector_features[map_id] = geojson_format.readFeatures(lines_and_polys);
vector_source[map_id] = new ol.source.Vector();
vector_source[map_id].addFeatures(vector_features[map_id]);
vector_layer[map_id] = new ol.layer.Vector({
source: vector_source[map_id],
style: get_vector_style
});
map[map_id].addLayer(vector_layer[map_id]);
if (lines_and_polys.features.length){
map_view[map_id].fit(vector_source[map_id].getExtent());
}
};
var display_geo_items = function(map_id, base_url, slug, pk,
disp_cr, disp_bf, get_poly) {
var url = base_url;
if (slug === "operation") {
url += "?operation_pk=";
}
else {
url += "?context_record_pk=";
}
url += pk;
httpRequest = new XMLHttpRequest();
if (!httpRequest) { return; }
httpRequest.onreadystatechange = function() {
if (httpRequest.readyState === XMLHttpRequest.DONE) {
if (httpRequest.status === 200) {
geo_items = to_geo_items(JSON.parse(httpRequest.responseText), slug, disp_cr, disp_bf)
console.log('geoitems')
console.log(geo_items)
if (current_test) {
geo_items_features[map_id] = [];
}
let bfs = null;
let crs = null;
if (get_poly) {
bfs = display_associated_polys(map_id, geo_items['base-finds'], 'basefind');
crs = display_associated_polys(map_id, geo_items['context-records'], 'contextrecord');
} else {
bfs = display_associated_points(map_id, geo_items['base-finds'], 'basefind');
crs = display_associated_points(map_id, geo_items['context-records'], 'contextrecord');
}
if (current_test) {
geo_items_features[map_id].push(bfs)
geo_items_features[map_id].push(crs)
$("#http-geo-items-ready-"+slug+"-"+pk).show();
}
} else { return; }
}
};
httpRequest.open('GET', url, true);
httpRequest.send();
};
var to_geo_items = function (obj, slug, disp_cr, disp_bf) {
const objects = {'context-records': {'features': [], 'type': 'FeatureCollection'},
'base-finds': {'features': [], 'type': 'FeatureCollection'}};
if (slug === "operation") {
const crs = obj['properties']['context-records'];
for (const cr of crs['features']) {
if (disp_bf) {console.log(cr);
for (const bf of cr['properties']['base-finds']['features']) {
objects['base-finds']['features'].push(bf)
}
}
delete cr['properties'];
if (disp_cr) {
objects['context-records']['features'].push(cr)
}
}
}
else {
for (const bf of obj['properties']['base-finds']['features']) {
objects['base-finds']['features'].push(bf)
}
}
return objects;
}
var display_associated_polys = function (map_id, polys, slug) {
_vector_features = geojson_format.readFeatures(polys);
_vector_source = new ol.source.Vector();
_vector_source.addFeatures(_vector_features);
style = get_associated_base_find_style;
if (slug === 'contextrecord') {
style = get_associated_context_record_style;
}
_vector_layer = new ol.layer.Vector({
source: _vector_source,
style: style
});
map[map_id].addLayer(_vector_layer);
return _vector_features
}
var get_associated_base_find_style = function (feature) {
return new ol.style.Style({
stroke: new ol.style.Stroke({
color: 'rgba(0, 0, 0, 1)',
width: 2
}),
fill: new ol.style.Fill({
color: 'rgba(O, O, O, 0.2)'
})
});
};
var get_associated_context_record_style = function (feature) {
return new ol.style.Style({
stroke: new ol.style.Stroke({
color: 'rgba(255, 255, 255, 1)',
width: 2
}),
fill: new ol.style.Fill({
color: 'rgba(255, 255, 255, 0.2)'
}),
radius: 10
});
};
var display_associated_points = function (map_id, points, slug) {
console.log('geo items points')
console.log(points);
_point_features = geojson_format.readFeatures(points);
_cluster_source = new ol.source.Cluster({
distance: 40,
source: new ol.source.Vector()
});
_cluster_source.getSource().addFeatures(_point_features);
// TODO: create own style
style = cluster_get_style;
if (slug === 'contextrecord') {
style = style;
}
_cluster_layer = new ol.layer.Vector({
name: 'Cluster',
source: _cluster_source,
style: style
});
map[map_id].addLayer(_cluster_layer);
return _point_features;
}
// for test purpose
// to insert in sheet_simple_map.html when working with maps
// wait_for_map("map-{{window_id}}-{{geo_item.SLUG}}-{{geo_item.pk}}");
var wait_for_map = function(map_id) {
map[map_id].on('rendercomplete', function(event) {
console.log("map fully loaded");
})
if (map_id in cluster_layer) {
console.log('cluster layer');
cluster_layer[map_id].on('change', function(event) {
if (cluster_layer.getState === "ready") {
console.log("cluster fully loaded");
}
})
}
if (map_id in vector_layer) {
console.log('vector layer');
vector_layer[map_id].on('change', function(event) {
if (vector_layer.getState === "ready") {
console.log("vector fully loaded");
}
})
}
if (map_id in cluster_source) {
console.log('cluster source');
cluster_source[map_id].on('change', function(event) {
if (cluster_source.getState === "ready") {
console.log("cluster source fully loaded");
}
})
}
if (map_id in vector_source) {
console.log('vector source');
vector_source[map_id].on('change', function(event) {
if (vector_source.getState === "ready") {
console.log("vector source fully loaded");
}
})
}
}
var get_map_by_id_test = function (id) {
// problem here: async function because map and layers are not fully loaded directly
map[id].getLayers().getArray()[0].getSource().on(
'change', function (evt) {
console.log('change')
var source = evt.target;
if (source.getState() === "ready") {
console.log('ready');
console.log(map_layers[id][0].getSource().getKeys())
}
}
)
map[id].on(
'change', function (evt) {
console.log('change')
var map = evt.target;
if (map.getState() === "ready") {
console.log('ready');
console.log(map_layers[id][0].getSource().getKeys())
}
}
)
console.log("vector_features")
console.log(vector_features[id])
console.log("layers")
console.log(map_layers[id])
console.log("source")
console.log(map_layers[id][0].getSource())
}
var get_features_by_id = function (id) {
if (vector_features[id] == null) {
var base_features = geojson_format.writeFeaturesObject(point_features[id], {decimals: 5});
} else {
var base_features = geojson_format.writeFeaturesObject(vector_features[id], {decimals: 5});
}
var geo_items_feats = [];
if (geo_items_features && id in geo_items_features) {
for (var features of geo_items_features[id]) {
geo_items_feats.push(geojson_format.writeFeaturesObject(features, {decimals: 5}));
}
}
return [base_features, geo_items_feats];
}
var base_features_type = function (id) {
return "vector " + typeof vector_features[id] + ", cluster " + typeof point_features[id] ;
}