/* 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.constructor = TrackPositionControl; TrackPositionControl.prototype.handleTrackPosition = function handleTrackPosition () { 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 = "complete 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 = ""; $("#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 () { geo_items_features = {}; current_test = true; if ($("#http-geo-items-ready").length === 0) { $("#display-geo-items").after('
Ready!
'); } $("#http-geo-items-ready").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); } //console.log('map init') //console.log(map[map_id]); display_points(map_id, points); display_lines_and_polys(map_id, lines_and_polys); //console.log('map after points'); //console.log(map[map_id]); 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){ //console.log('points'); //console.log(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) if (current_test) { geo_items_features[map_id] = []; } var feat = null; for (geo_item of geo_items['base-finds']) { if (get_poly) { feat = display_associated_polys(map_id, geo_item, 'basefind'); } else { feat = display_associated_points(map_id, geo_item, 'basefind'); } if (current_test) { geo_items_features[map_id].push(feat); } } for (geo_item of geo_items['context-records']) { if (get_poly) { feat = display_associated_polys(map_id, geo_item, 'contextrecord'); } else { feat = display_associated_points(map_id, geo_item, 'contextrecord'); } if (current_test) { geo_items_features[map_id].push(feat); } } if (current_test) { $("#http-geo-items-ready").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': [], 'base-finds': []}; if (slug === "operation") { const crs = obj['properties']['context-records']; for (const cr of crs['features']) { if (disp_bf) { objects['base-finds'].push(cr['properties']['base-finds']) } delete cr['properties']; } if (disp_cr) { objects['context-records'].push(crs); } } else { objects['base-finds'].push(obj['properties']['base-finds']) } 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: 3}); } else { var base_features = geojson_format.writeFeaturesObject(vector_features[id], {decimals: 3}); } 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: 3})); } } return [base_features, geo_items_feats]; } var print = function (id) { return "vector " + typeof vector_features[id] + ", cluster " + typeof point_features[id] ; }