Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions backend/gn_module_monitoring/conf_schema_toml.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ class GnModuleSchemaConf(Schema):
keys=fields.Str(), values=fields.Str(), load_default=PERMISSION_LEVEL_DEFAULT
)

ENABLE_LEAFLET_CLUSTER = fields.Boolean(load_default=False)


# AREA_TYPE = fields.List(fields.String(), missing=["COM", "M1", "M5", "M10"])
# BORNE_OBS = fields.List(fields.Integer(), missing=[1, 20, 40, 60, 80, 100, 120])
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,3 +75,9 @@
.custom-dt {
box-shadow: none !important;
}

:host ::ng-deep pnx-map:focus,
:host ::ng-deep pnx-map:focus-visible,
:host ::ng-deep .leaflet-container:focus {
outline: none;
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
:host ::ng-deep .hide-draw-form .leaflet-top.leaflet-left {
display: none;
}

:host ::ng-deep pnx-map:focus,
:host ::ng-deep pnx-map:focus-visible,
:host ::ng-deep .leaflet-container:focus {
outline: none;
}
5 changes: 5 additions & 0 deletions frontend/app/services/config.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,11 @@ export class ConfigService {
codeListObservers() {
return this.appConfig.MONITORINGS.CODE_OBSERVERS_LIST;
}

get enableClustering(): boolean {
return this.appConfig.MONITORINGS.ENABLE_LEAFLET_CLUSTER;
}

/** Frontend Module Monitoring Url */
frontendModuleMonitoringUrl() {
return this._moduleService.currentModule.module_path;
Expand Down
177 changes: 148 additions & 29 deletions frontend/app/services/geojson.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { MapService } from '@geonature_common/map/map.service';

import { SitesService, SitesGroupService } from './api-geom.service';
import { FormService } from './form.service';
import { ConfigService } from './config.service';

// This service will be used for sites and sites groups

Expand Down Expand Up @@ -76,8 +77,11 @@ export class GeoJSONService {
private _sites_group_service: SitesGroupService,
private _sites_service: SitesService,
private _mapService: MapService,
private _formService: FormService
) {}
private _formService: FormService,
public _configService: ConfigService
) {
this.sitesFeatureGroup = this._createSitesLayerGroup();
}

setModuleCode(moduleCode: string) {
this._sites_group_service.setModuleCode(moduleCode);
Expand Down Expand Up @@ -161,7 +165,8 @@ export class GeoJSONService {
onEachFeature,
cfg.layerName,
cfg.zoom,
effectiveStyle
effectiveStyle,
false
);
});
}
Expand Down Expand Up @@ -191,33 +196,78 @@ export class GeoJSONService {
}

// setGeomSiteGroupFromExistingObject(geom, name:boolean = false) {
// this.sitesGroupFeatureGroup = this.setMapData(geom, () => {}, name ? NAME_LAYER_SITE : null, null);
// this.sitesGroupFeatureGroup = this.setMapData(geom, () => {}, name ? NAME_LAYER_SITE : null, null, false);
// }

setMapData(
geojson: GeoJSON.Geometry | GeoJSON.FeatureCollection,
onEachFeature: Function,
layerName: string | null,
zoom: boolean = true,
style?
style?,
enableClustering: boolean = this._configService.enableClustering
): L.FeatureGroup | undefined {
const map = this._mapService.getMap();
if (geojson['features'] == null) {
return undefined;
}

const layer: L.Layer = this._mapService.createGeojson(geojson, false, onEachFeature, style);
const featureGroup = new L.FeatureGroup();
featureGroup.addLayer(layer);
this._mapService.map.addLayer(featureGroup);
const layer = this._mapService.createGeojson(geojson, false, onEachFeature, style);

if (enableClustering) {
const clusterGroup = this._createMarkerClusterGroup();
const mixedGroup = new L.FeatureGroup();
let hasClusterLayer = false;
let hasNonClusterLayer = false;
layer.eachLayer((geoLayer) => {
if (geoLayer instanceof L.Marker || geoLayer instanceof L.CircleMarker) {
clusterGroup.addLayer(geoLayer);
hasClusterLayer = true;
return;
}
mixedGroup.addLayer(geoLayer);
hasNonClusterLayer = true;
});

if (layerName) {
this._mapService.layerControl.addOverlay(featureGroup, layerName);
}
if (zoom) {
map.fitBounds(featureGroup.getBounds());
if (hasClusterLayer) {
mixedGroup.addLayer(clusterGroup);
}
this.sitesFeatureGroup = hasClusterLayer && !hasNonClusterLayer ? clusterGroup : mixedGroup;

if (this.sitesFeatureGroup.getLayers().length > 0) {
this._mapService.map.addLayer(this.sitesFeatureGroup);
if (layerName) {
this._mapService.layerControl.addOverlay(this.sitesFeatureGroup, layerName);
}
if (zoom) {
map.fitBounds(this.sitesFeatureGroup.getBounds());
}
}
} else {
this.sitesFeatureGroup = new L.FeatureGroup();
this.sitesFeatureGroup.addLayer(layer);
this._mapService.map.addLayer(this.sitesFeatureGroup);
if (layerName) {
this._mapService.layerControl.addOverlay(this.sitesFeatureGroup, layerName);
}
if (zoom) {
map.fitBounds(this.sitesFeatureGroup.getBounds());
}
}
return featureGroup;

return this.sitesFeatureGroup;

// const featureGroup = new L.FeatureGroup();
// featureGroup.addLayer(layer);
// this._mapService.map.addLayer(featureGroup);

// if (layerName) {
// this._mapService.layerControl.addOverlay(featureGroup, layerName);
// }
// if (zoom) {
// map.fitBounds(featureGroup.getBounds());
// }
// return featureGroup;
}

setMapDataWithFeatureGroup(featureGroup: L.FeatureGroup[]) {
Expand Down Expand Up @@ -255,7 +305,7 @@ export class GeoJSONService {
// );
// this.geojsonSitesGroups.features = features;
// this.removeFeatureGroup(this.sitesGroupFeatureGroup);
// this.setMapData(this.geojsonSitesGroups, this.onEachFeature, null, defaultSiteGroupStyle);
// this.setMapData(this.geojsonSitesGroups, this.onEachFeature, null, defaultSiteGroupStyle, false);
// }
// }

Expand Down Expand Up @@ -284,23 +334,61 @@ export class GeoJSONService {
}

selectSitesLayer(id: number, zoom: boolean) {
const layers = this.sitesFeatureGroup.eachLayer((layer) => {
if (layer instanceof L.GeoJSON) {
layer.eachLayer((sublayer: L.GeoJSON) => {
const feature = sublayer.feature as GeoJSON.Feature;
if (feature.properties['id_base_site'] == id) {
if (zoom == true) {
const featureGroup = new L.FeatureGroup();
featureGroup.addLayer(sublayer);
this._mapService.map.fitBounds(featureGroup.getBounds());
}
sublayer.openPopup();
let foundLayer: L.FeatureGroup | null = null;

const trySelect = (layer: any, getLatLng?: () => L.LatLng) => {
const feature = layer.feature as GeoJSON.Feature;
if (feature.properties['id_base_site'] == id) {
if (zoom) {
if (getLatLng) {
this._mapService.map.setView(getLatLng(), 18);
} else {
const fg = new L.FeatureGroup([layer]);
this._mapService.map.fitBounds(fg.getBounds());
}
}
layer.openPopup();
foundLayer = new L.FeatureGroup([layer]);
}
};

if (this.sitesFeatureGroup) {
if (this.sitesFeatureGroup instanceof L.MarkerClusterGroup) {
this.sitesFeatureGroup.getAllChildMarkers().forEach((marker: any) => {
if (foundLayer) return;
trySelect(marker, () => marker.getLatLng());
});
} else {
this.sitesFeatureGroup.eachLayer((layer: any) => {
if (foundLayer) return;

if (layer instanceof L.GeoJSON) {
layer.eachLayer((sublayer: any) => {
if (foundLayer) return;
trySelect(sublayer);
});
return;
}

if (layer instanceof L.MarkerClusterGroup) {
layer.getAllChildMarkers().forEach((marker: any) => {
if (foundLayer) return;
trySelect(marker, () => marker.getLatLng());
});
return;
}

if (layer instanceof L.Marker || layer instanceof L.CircleMarker) {
trySelect(layer, () => layer.getLatLng());
return;
}

trySelect(layer);
});
}
});
return layers;
}

return foundLayer;
}

removeAllFeatureGroup() {
Expand Down Expand Up @@ -365,4 +453,35 @@ export class GeoJSONService {
}
return false;
}

clusterCountOverrideFn(cluster) {
const obsChildCount = cluster.getChildCount();
const clusterSize = obsChildCount > 100 ? 'large' : obsChildCount > 10 ? 'medium' : 'small';
return L.divIcon({
html: `<div><span>${obsChildCount}</span></div>`,
className: `marker-cluster marker-cluster-${clusterSize}`,
iconSize: L.point(40, 40),
});
}

private _createSitesLayerGroup(): L.FeatureGroup | L.MarkerClusterGroup {
return this._configService.enableClustering
? this._createMarkerClusterGroup()
: new L.FeatureGroup();
}

private _createMarkerClusterGroup(): L.MarkerClusterGroup {
return L.markerClusterGroup({
disableClusteringAtZoom: 18,
spiderfyOnMaxZoom: true,
showCoverageOnHover: true,
zoomToBoundsOnClick: true,
maxClusterRadius: (zoom) => {
if (zoom >= 15) return 50;
if (zoom >= 12) return 100;
return 200;
},
iconCreateFunction: this.clusterCountOverrideFn,
});
}
}
5 changes: 4 additions & 1 deletion monitorings_config.toml.example
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,7 @@
#TITLE_MODULE = "Module de suivi"

# code of the observator list -- utilisateurs.t_listes
#CODE_OBSERVERS_LIST = "obsocctax"
#CODE_OBSERVERS_LIST = "obsocctax"

# Activer (=true) ou pas (=false) la clusterisation des objets sur la carte
#ENABLE_LEAFLET_CLUSTER = false