Non puoi selezionare più di 25 argomenti
Gli argomenti devono iniziare con una lettera o un numero, possono includere trattini ('-') e possono essere lunghi fino a 35 caratteri.
373 righe
18 KiB
373 righe
18 KiB
<?php echo $this->Html->css('/node_modules/ol/ol'); ?> |
|
<?php echo $this->Html->css('/ol-ext/ol-ext.min'); ?> |
|
<?php echo $this->Html->css('jixel_ol'); ?> |
|
<div class="modal fade" id="geocode-results-modal" style="display: none;" aria-hidden="true"> |
|
<div class="modal-dialog"> |
|
<div class="modal-content"> |
|
<div id="geocode-results-modal-header" class="modal-header"> |
|
<h4 class="modal-title"><?= __('Scegli un risultato:') ?></h4> |
|
<button type="button" class="close" data-dismiss="modal" aria-label="Close"> |
|
<span aria-hidden="true">×</span> |
|
</button> |
|
</div> |
|
<div id="geocode-results-modal-body" class="modal-body"></div> |
|
<div id="geocode-results-modal-footer" class="modal-footer justify-content-between"></div> |
|
</div> |
|
</div> |
|
</div> |
|
<div id="olmap" class="olmap"></div> |
|
<div id="jixel-popup" class="jixel-ol-popup"> |
|
<div> |
|
<div id="jixel-popup-header"></div> |
|
<a href="#" id="jixel-popup-closer" class="jixel-ol-popup-closer"></a> |
|
</div> |
|
<div id="jixel-popup-content" class="jixel-ol-popup-content"></div> |
|
</div> |
|
<?php if ($tools): ?> |
|
<div id="jixel-tools-element" class="jixel-ol-tools-button ol-unselectable ol-control"> |
|
<?php if ($geocoding): ?> |
|
<button type="button" id="jixel-address-button" onclick="geocode(prompt('<?= __('Inserisci un indirizzo o le coordinate (in formato EPSG:4326)') ?>'))"> |
|
<i class="fa fa-location-arrow" title="<?= __('Cerca un indirizzo') ?>"></i> |
|
</button> |
|
<?php endif; ?> |
|
<?php if ($point): ?> |
|
<button type="button" id="jixel-point-button" onclick="toggle_tool('point')"> |
|
<i class="fa fa-map-marker" title="<?= __('Inserisci un punto in mappa') ?>"></i> |
|
</button> |
|
<?php endif; ?> |
|
<?php if ($polygon): ?> |
|
<button type="button" id="jixel-polygon-button" onclick="toggle_tool('polygon')"> |
|
<i class="fa fa-map" title="<?= __('Inserisci un poligono in mappa') ?>"></i> |
|
</button> |
|
<?php endif; ?> |
|
<?php if ($circle): ?> |
|
<button type="button" id="jixel-circle-button" onclick="toggle_tool('circle')"> |
|
<i class="fa fa-circle" title="<?= __('Inserisci un cerchio in mappa') ?>"></i> |
|
</button> |
|
<?php endif; ?> |
|
<?php if (($point || $polygon || $circle) && $delete_geometry): ?> |
|
<button type="button" id="jixel-delete-geometry-button" onclick="toggle_tool('delete-geometry')"> |
|
<i class="fa fa-trash" title="<?= __('Cancella una geometria') ?>"></i> |
|
</button> |
|
<?php endif; ?> |
|
</div> |
|
<?php endif; ?> |
|
<?php echo $this->Html->script('/node_modules/ol/dist/ol'); ?> |
|
<?php echo $this->Html->script('/ol-ext/ol-ext.min'); ?> |
|
<?php echo $this->Html->script('moment'); ?> |
|
<?php echo $this->Html->script('jixel-primitives'); ?> |
|
<script> |
|
var createLoaderFunction = (vectorSource, layer_url) => { |
|
return (extent, resolution, projection) => { |
|
let proj = projection.getCode(); |
|
let url = layer_url + '&srsname=' + proj + '&bbox=' + extent.join(','); |
|
console.log("chiamato loader con url: " + url); |
|
vectorSource.last_request_identifier = Date.now(); |
|
makeGetRequest(url, {identifier: vectorSource.last_request_identifier}) |
|
.then((response) => { |
|
if (response.identifier == vectorSource.last_request_identifier) { |
|
const result = response.response; |
|
vectorSource.addFeatures(vectorSource.getFormat().readFeatures(result)); |
|
} |
|
if (vectorSource.describe_feature_type != undefined) return vectorSource.describe_feature_type; |
|
const layer_url_parts = layer_url.split('GetFeature'); |
|
const describe_feature_type_url = layer_url_parts[0] + 'DescribeFeatureType' + layer_url_parts[1]; |
|
return makeGetRequest(describe_feature_type_url); |
|
}) |
|
.then((describe_feature_type) => { |
|
vectorSource.describe_feature_type = describe_feature_type; |
|
vectorSource.describe_feature_properties = vectorSource.describe_feature_type.featureTypes[0].properties.reduce((acc, property) => { |
|
acc[property.name] = property.localType; |
|
return acc; |
|
}, {}); |
|
}) |
|
.catch((error) => { |
|
console.log('createLoaderFunction error:'); |
|
console.log(error); |
|
vectorSource.removeLoadedExtent(extent); |
|
}); |
|
}; |
|
}; |
|
|
|
var createVectorSource = (layer_url) => { |
|
let vectorSource = new ol.source.Vector({ |
|
format: new ol.format.GeoJSON(), |
|
strategy: ol.loadingstrategy.bbox, |
|
}); |
|
vectorSource.load = function (url) { |
|
console.log("chiamato load con url: " + url); |
|
this.last_request_identifier = Date.now(); |
|
makeGetRequest(url, {identifier: this.last_request_identifier}) |
|
.then((response) => { |
|
if (response.identifier == this.last_request_identifier) { |
|
const result = response.response; |
|
const chunk = 20; // max numero di elementi per chunk |
|
let to_be_deleted_features_chuncked = []; |
|
this.getFeatures().filter((feature) => { |
|
return result.features.find(new_feature => new_feature.id === feature.getId()) === undefined; |
|
}) |
|
.forEach((to_be_deleted_feature, index) => { |
|
let cdf_index = Math.floor(index / chunk); |
|
to_be_deleted_features_chuncked[cdf_index] !== undefined || (to_be_deleted_features_chuncked[cdf_index] = []); |
|
to_be_deleted_features_chuncked[cdf_index].push(to_be_deleted_feature); |
|
}); |
|
to_be_deleted_features_chuncked.forEach((chunk, index) => { |
|
setTimeout(() => { |
|
chunk.forEach(feature => this.removeFeature(feature)); |
|
}, 1000 * index); |
|
}); |
|
this.addFeatures(this.getFormat().readFeatures(result)); |
|
} |
|
}) |
|
.catch((error) => { |
|
console.log('vectorSource load error:'); |
|
console.log(error); |
|
}); |
|
}; |
|
vectorSource.setLoader( |
|
createLoaderFunction(vectorSource, layer_url) |
|
); |
|
return vectorSource; |
|
}; |
|
|
|
var createClusteredSource = (vectorSource) => { |
|
return new ol.source.Cluster({ |
|
distance: 50, |
|
source: vectorSource, |
|
geometryFunction: (feature) => { |
|
let geometry = feature.getGeometry(); |
|
if (geometry.getType() == 'Point') { |
|
return geometry; |
|
} |
|
if (geometry.getType() == 'Polygon') { |
|
return geometry.getInteriorPoint(); |
|
} |
|
if (geometry.getType() == 'GeometryCollection') { |
|
let pointGeom = null; |
|
geometry.getGeometries().forEach((geom) => { |
|
if (geom.getType() == 'Point') { |
|
pointGeom = geom; |
|
} |
|
}); |
|
return pointGeom; |
|
} |
|
return null; |
|
}, |
|
}); |
|
}; |
|
|
|
var styleFunctionGenerator = (style_object) => { |
|
return (feature, resolution) => { |
|
if (style_object == null) { |
|
// se non è previsto uno stile, restituiamo il default: |
|
return new ol.layer.Vector().getStyleFunction()(); |
|
} |
|
let size = feature.get('features') != undefined ? feature.get('features').length : 1; |
|
if (size > 1) { |
|
let image = null; |
|
if (style_object.cluster.image.type == 'circle') { |
|
image = new ol.style.Circle({ |
|
radius: 20, |
|
stroke: new ol.style.Stroke({ |
|
color: style_object.cluster.image.circle.stroke.color, |
|
}), |
|
fill: new ol.style.Fill({ |
|
color: style_object.cluster.image.circle.fill.color, |
|
}) |
|
}); |
|
} |
|
if (style_object.cluster.image.type == 'chart') { |
|
let data = []; |
|
let colors = []; |
|
style_object.cluster.image.chart.sections.forEach((section) => { |
|
data.push(feature.get('features').filter((feature) => { return section.data.values.includes(feature.get(section.data.field)); }).length); |
|
colors.push(ol.color.asString(section.color)); |
|
}); |
|
image = new ol.style.Chart({ |
|
type: style_object.cluster.image.chart.type, |
|
radius: 20, |
|
data: data, |
|
colors: colors, |
|
rotateWithView: true, |
|
stroke: new ol.style.Stroke({ |
|
color: style_object.cluster.image.chart.stroke.color, |
|
width: style_object.cluster.image.chart.stroke.width, |
|
}), |
|
}); |
|
} |
|
return new ol.style.Style({ |
|
image: image, |
|
text: new ol.style.Text({ |
|
text: size.toString(), |
|
font: '12px sans-serif', |
|
fill: new ol.style.Fill({ |
|
color: style_object.cluster.text.fill.color, |
|
}), |
|
backgroundFill: new ol.style.Fill({ |
|
color: style_object.cluster.text.backgroundFill.color, |
|
}), |
|
}), |
|
}); |
|
} else { |
|
// se il layer è clusterizzato: recuperiamo la feature originale che è l'unica attaccata all'array features! |
|
// altrimenti prendiamo direttamente la feature passata |
|
const feature_for_style = feature.get('features') == undefined ? feature : feature.get('features')[0]; |
|
// se nello style_object passato è definito un filtro, recuperiamo il campo, il valore e quindi |
|
// lo stile (al momento solo il color indicizzato dal valore del campo configurato) |
|
if (style_object.filter != undefined) { |
|
const filter_field_value = feature_for_style.get(style_object.filter.field); |
|
const filter_color = style_object.filter.values[filter_field_value].color; |
|
if (filter_color != undefined) { |
|
style_object.fill.color = filter_color; |
|
style_object.image.fill.color = filter_color; |
|
} |
|
} |
|
return new ol.style.Style({ |
|
geometry: (feature) => { |
|
return feature_for_style.getGeometry(); |
|
}, |
|
fill: new ol.style.Fill({ |
|
color: style_object.fill.color, |
|
}), |
|
stroke: new ol.style.Stroke({ |
|
color: style_object.stroke.color, |
|
}), |
|
image: new ol.style.Circle({ |
|
radius: 10, |
|
stroke: new ol.style.Stroke({ |
|
color: style_object.image.stroke.color, |
|
}), |
|
fill: new ol.style.Fill({ |
|
color: style_object.image.fill.color, |
|
}) |
|
}), |
|
}); |
|
} |
|
} |
|
}; |
|
|
|
var selectedStyleFunctionGenerator = () => { |
|
return (feature, resolution) => { |
|
let size = feature.get('features') != undefined ? feature.get('features').length : 1; |
|
if (size > 1) { |
|
return new ol.style.Style({ |
|
image: new ol.style.Circle({ |
|
radius: 20, |
|
stroke: new ol.style.Stroke({ |
|
color: [255, 255, 255, 1], |
|
}), |
|
fill: new ol.style.Fill({ |
|
color: [0, 0, 255, 0.8], |
|
}) |
|
}), |
|
text: new ol.style.Text({ |
|
text: size.toString(), |
|
font: '12px sans-serif', |
|
fill: new ol.style.Fill({ |
|
color: [255,255,255,1], |
|
}) |
|
}) |
|
}); |
|
} else { |
|
return new ol.style.Style({ |
|
geometry: (feature) => { |
|
// se il layer è clusterizzato: recuperiamo la geometria originale che è l'unica attaccata all'array features! |
|
// altrimenti prendiamo la geometria della feature passata |
|
let feature_for_style = feature.get('features') == undefined ? feature : feature.get('features')[0]; |
|
return feature_for_style.getGeometry(); |
|
}, |
|
fill: new ol.style.Fill({ |
|
color: [0, 0, 255, 0.8], |
|
}), |
|
stroke: new ol.style.Stroke({ |
|
color: [255,255,255,1], |
|
}), |
|
image: new ol.style.Circle({ |
|
radius: 10, |
|
stroke: new ol.style.Stroke({ |
|
color: [255,255,255,1], |
|
}), |
|
fill: new ol.style.Fill({ |
|
color: [0, 0, 255, 0.8], |
|
}) |
|
}), |
|
}); |
|
} |
|
}; |
|
}; |
|
|
|
var createLayer = (layer_info) => { |
|
let vector_source = createVectorSource(layer_info.url_layer); |
|
if (layer_info.clusterize) { |
|
vector_source = createClusteredSource(vector_source); |
|
} |
|
const layer = new ol.layer.Vector({ |
|
id: layer_info.id, |
|
code: layer_info.code, |
|
name: layer_info.name, |
|
url_layer: layer_info.url_layer, |
|
clusterized: layer_info.clusterize, |
|
visible: layer_info.visible, |
|
source: vector_source, |
|
style: styleFunctionGenerator(layer_info.style), // generiamo la style function per le features |
|
zoom_to_layer: layer_info.zoom_to_layer, |
|
}); |
|
|
|
layer.buildCQLFilter = function (...conditions) { |
|
let valid_conditions = []; |
|
conditions.forEach((condition) => { |
|
typeof condition !== 'string' || condition.length === 0 || (valid_conditions.push(condition)); |
|
}); |
|
console.log('buildCQLFilter received conditions:'); |
|
console.log(conditions); |
|
console.log('buildCQLFilter valid_conditions:'); |
|
console.log(valid_conditions); |
|
cql_filter = valid_conditions.length > 0 ? encodeURI('query_filter=' + valid_conditions.join(' AND ')) : ''; |
|
console.log('buildCQLFilter returned cql_filter:'); |
|
console.log(cql_filter); |
|
return cql_filter; |
|
} |
|
|
|
layer.set_layer_source_url = function (url, append, soft) { |
|
let new_url = undefined; |
|
if (typeof url != 'string' || url.length == 0) { |
|
new_url = this.get('url_layer'); // se non passiamo url, ripristiniamo la default |
|
} else { |
|
const append_url = (typeof append == 'boolean') ? append : false; |
|
// creiamo la nuova url come default + '&' + url o come url: |
|
new_url = append_url ? (this.get('url_layer') + '&' + url) : url; |
|
} |
|
const vector_source = this.get('clusterized') ? this.getSource().getSource() : this.getSource(); |
|
vector_source.setLoader(createLoaderFunction(vector_source, new_url)); |
|
if (typeof soft === 'boolean' && soft) { |
|
vector_source.load(new_url); // eliminiamo le vecchie features e aggiungiamo le nuove in maniera soft (nessun glitch) ma molto + lento! |
|
} else { |
|
vector_source.refresh(); // forziamo il reload delle features dal server |
|
} |
|
console.log("set_layer_source_url:"); |
|
console.log(new_url); |
|
return this; |
|
}; |
|
return layer; |
|
} |
|
|
|
var generatePopupEntryFromFeature = (feature, describe_feature_properties) => { |
|
// non inseriamo nel popup alcune proprietà "speciali" |
|
const popup_mask_keys = ["geometry"]; |
|
let body = '<br>'; |
|
feature.getKeys().forEach((key) => { |
|
if (!popup_mask_keys.includes(key) && !key.startsWith("hidden_") && feature.get(key) != null) { |
|
const property_type = describe_feature_properties[key]; |
|
let key_name = key.ununderscore(); |
|
if (property_type == 'date-time' || property_type == 'date') { |
|
let format = property_type == "date-time" ? "DD/MM/YYYY HH:mm" : "DD/MM/YYYY"; |
|
let date = property_type == "date-time" ? feature.get(key) : feature.get(key).split('Z')[0]; |
|
body += '<b>' + key_name + ':</b> ' + moment.utc(date).format(format) + '<br>'; |
|
} else { |
|
body += '<b>' + key_name + ':</b> ' + feature.get(key) + '<br>'; |
|
} |
|
} |
|
}); |
|
return body + '<hr>'; |
|
}; |
|
</script>
|
|
|