Skip to content

Source IdentifyAdvanced

Source IdentifyAdvanced

Cette page est générée automatiquement à partir du dépôt local au moment de la génération de la documentation.

Fichiers inclus

packages/common/src/lib/widgets/identify-advanced/identify-advanced.declaration.ts

packages/common/src/lib/widgets/identify-advanced/identify-advanced.declaration.ts
import { widgetFactorySvelte, type WidgetProps } from '$lib/api/managers/widget';
import { type IdentifyAdvancedFullConfig, identifyAdvancedFullConfigSchema } from './identify-advanced.config';
import type { WidgetDeclaration } from '$lib/api/managers/widget/widget-declaration';
import { IdentifyAdvancedState } from '$lib/widgets/identify-advanced/identify-advanced.state.svelte';
export const declaration = {
factory: () =>
import('./IdentifyAdvanced.svelte').then((IdentifyAdvanced) => widgetFactorySvelte(IdentifyAdvanced)),
schema: () => identifyAdvancedFullConfigSchema,
state: (props) => new IdentifyAdvancedState(props),
} satisfies WidgetDeclaration;
export type IdentifyAdvancedProps = WidgetProps<IdentifyAdvancedFullConfig, IdentifyAdvancedState>;

packages/common/src/lib/widgets/identify-advanced/identify-advanced.config.ts

packages/common/src/lib/widgets/identify-advanced/identify-advanced.config.ts
import { z } from 'zod';
import { i18nSchemaFrom } from '$lib/api/managers/i18n';
import { identifyAdvancedI18n } from '$lib/widgets/identify-advanced/identify-advanced.i18n';
import { defineWidgetConfig, inToolbarSchemaFrom } from '$lib/api/managers/configuration';
import { colorfulDrawFeatureSymbolsSchema, defaultHighlightSymbolColor } from '$lib/api/symbol';
export const identifyAdvancedFullConfigSchema = defineWidgetConfig({
i18n: i18nSchemaFrom(identifyAdvancedI18n),
title: identifyAdvancedI18n.title,
inToolbar: inToolbarSchemaFrom({
type: 'button',
}),
icon: {
lucide: 'Info',
},
container: {
id: 'dialog',
draggable: true,
minWidth: '25vw',
center: true,
},
config: z.object({
dataTableWidgetId: z.string(),
defaultSymbols: colorfulDrawFeatureSymbolsSchema(defaultHighlightSymbolColor),
layerListMaxHeight: z.string().default('100vh'),
}),
});
export type IdentifyAdvancedFullConfig = z.infer<typeof identifyAdvancedFullConfigSchema>;
export type IdentifyAdvancedI18n = IdentifyAdvancedFullConfig['i18n'];

packages/common/src/lib/widgets/identify-advanced/identify-advanced.i18n.ts

packages/common/src/lib/widgets/identify-advanced/identify-advanced.i18n.ts
import type { I18nRegistry } from '$lib/api/managers/i18n';
export const identifyAdvancedI18n = {
title: {
fr: 'Identification avancée',
nl: 'NL - Identification avancée',
},
'identify-advanced.geometry-title': {
fr: 'Dessiner une géométrie',
nl: 'NL - Dessiner une géométrie',
},
'identify-advanced.buffer-title': {
fr: 'Buffer',
nl: 'NL - Buffer',
},
'identify-advanced.layers-title': {
fr: 'Couches',
nl: 'NL - Couches',
},
'identify-advanced.layer-name': {
fr: "Couche d'identification avancée",
nl: "NL - Couche d'identification avancée",
},
'identify-advanced.missing-draw': {
fr: "Veuillez réaliser un dessin avant de lancer l'identification",
nl: "NL - Veuillez réaliser un dessin avant de lancer l'identification",
},
'identify-advanced.missing-services': {
fr: "Veuillez sélectionner au moins un service avant de lancer l'identification",
nl: "NL - Veuillez sélectionner au moins un service avant de lancer l'identification",
},
'identify-advanced.validate-selection': {
fr: 'Valider la sélection',
nl: 'NL - Valider la sélection',
},
'identify-advanced.buffer': {
fr: 'Largeur (m)',
nl: 'NL - Largeur (m)',
},
} satisfies I18nRegistry;

packages/common/src/lib/widgets/identify-advanced/identify-advanced.model.ts

packages/common/src/lib/widgets/identify-advanced/identify-advanced.model.ts
import { type DrawCreateType } from '$lib/api/tools';
import type { CommonI18nKeys } from '$lib/api/managers/i18n';
import type { Icon } from '$lib/api/icons';
import type { ApiSublayer } from '$lib/api/layers';
import type { ApiMapService } from '$lib/api/mapservices';
export type ServiceOrLayer = ApiMapService | ApiSublayer;
export type IdentifyAdvancedDrawOption = {
type: DrawCreateType;
title: CommonI18nKeys;
icon: Icon;
};
export const identifyAdvancedDrawOptions: IdentifyAdvancedDrawOption[] = [
{
type: 'point',
title: 'common.geometry.point',
icon: {
lucide: 'Dot',
},
},
{
type: 'polyline',
title: 'common.geometry.line',
icon: {
lucide: 'Waypoints',
},
},
{
type: 'polygon',
title: 'common.geometry.polygon',
icon: {
geoviewer: 'geoviewer-polygon',
},
},
{
type: 'circle',
title: 'common.geometry.circle',
icon: {
lucide: 'Circle',
},
},
];

packages/common/src/lib/widgets/identify-advanced/identify-advanced.state.svelte.ts

packages/common/src/lib/widgets/identify-advanced/identify-advanced.state.svelte.ts
import type { WidgetStateProps } from '$lib/api/managers/widget/widget-declaration';
import type { ApiSublayer } from '$lib/api/layers';
export class IdentifyAdvancedState {
public readonly servicesSelection: Map<ApiSublayer, IdentifyAdvancedItemState> = new Map();
constructor(private readonly props: WidgetStateProps) {}
public add(service: ApiSublayer) {
if (this.servicesSelection.has(service)) {
return this.servicesSelection.get(service)!;
}
const state = new IdentifyAdvancedItemState();
this.servicesSelection.set(service, state);
return state;
}
}
export class IdentifyAdvancedItemState {
checked = $state(false);
}

packages/common/src/lib/widgets/identify-advanced/IdentifyAdvanced.svelte

packages/common/src/lib/widgets/identify-advanced/IdentifyAdvanced.svelte
<script lang="ts">
import type { IdentifyAdvancedProps } from '$lib/widgets/identify-advanced/identify-advanced.declaration';
import { getI18n } from '$lib/api/managers/i18n';
import { initGraphicMapServiceConfiguration } from '$lib/api/managers/configuration';
import { getMapManager } from '$lib/api/map';
import { onDestroy } from 'svelte';
import type { DrawCreateType } from '$lib/api/tools';
import { identifyAdvancedDrawOptions } from '$lib/widgets/identify-advanced/identify-advanced.model';
import { Icon } from '$lib/components/icon';
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '$lib/components/shadcn/ui/accordion';
import { getLayerLeafs, hasSublayers } from '$lib/api/layers';
import IdentifyAdvancedLayerSelect from '$lib/widgets/identify-advanced/IdentifyAdvancedLayerSelect.svelte';
import { Button } from '$lib/components/shadcn/ui/button';
import type ServiceDataTableWidget from '$lib/widgets/service-data-table/ServiceDataTableWidget.svelte';
import { getWidgetManager } from '$lib/api/managers/widget';
import { isQueryable, type Queryable, recordToCssStyle, reverse } from '$lib/api/utils';
import type { ApiFeature } from '$lib/api/feature';
import { showToast } from '$lib/components/toast/toast.utils';
import { renderSnippet } from '$lib/components/magical-svelte-renderer/magical-svelte-renderer-helpers';
import { Label } from '$lib/components/shadcn/ui/label';
import { Input } from '$lib/components/shadcn/ui/input';
import type { BufferSymbol } from '$lib/api/symbol';
import type { ServiceDataTableState } from '$lib/widgets/service-data-table/service-data-table.state.svelte';
import { Tabs, TabsList, TabsTrigger } from '$lib/components/shadcn/ui/tabs';
let { fullConfig, state: widgetState }: IdentifyAdvancedProps = $props();
let { config } = fullConfig;
const i18n = getI18n(fullConfig.i18n);
const mapManager = getMapManager();
const widgetManager = getWidgetManager();
const layerList = mapManager.layerList;
const drawFactory = mapManager.tools.draw;
const geometryEngine = mapManager.tools.geometryEngine;
const zoom = mapManager.tools.zoom;
const serviceIdentifier = 'IdentifyAdvancedLayerId';
const identifyAdvancedGraphicService = mapManager.addGraphicMapService(
initGraphicMapServiceConfiguration({
label: i18n('identify-advanced.layer-name'),
id: serviceIdentifier,
toc: {
visible: false,
},
symbols: config.defaultSymbols,
}),
);
const drawTool = drawFactory.create({
layer: identifyAdvancedGraphicService,
defaultSymbols: config.defaultSymbols,
});
const services = $derived.by(() =>
layerList.list.filter((service) => {
const visibleAndHasSublayers = service.toc.visible && service.visible && hasSublayers(service);
const layerLeafs = getLayerLeafs(service);
return visibleAndHasSublayers && layerLeafs.some((leaf) => isQueryable(leaf));
}),
);
const serviceIds = $derived(services.map((service) => service.id));
let activeDraw = $state<DrawCreateType>();
let currentBuffer = $state<number>(0);
let bufferSymbol: BufferSymbol = $derived({
enabled: currentBuffer > 0,
distance: currentBuffer,
repeat: 1,
showLabel: true,
});
let currentFeature: ApiFeature | undefined;
$effect(() => {
drawTool.stop();
if (activeDraw) {
drawTool.create({
type: activeDraw,
onDrawComplete: (feature: ApiFeature) => {
updateCurrentFeature(feature);
activeDraw = null as unknown as undefined;
},
});
}
});
$effect(() => {
const visibleElements = { buffer: bufferSymbol };
drawTool.defaultSymbols = {
point: { ...config.defaultSymbols.point, visibleElements },
polyline: { ...config.defaultSymbols.polyline, visibleElements },
polygon: { ...config.defaultSymbols.polygon, visibleElements },
circle: { ...config.defaultSymbols.circle, visibleElements },
};
if (currentFeature) {
// @ts-expect-error ts do not accept deconstructive symbol setter
currentFeature.symbol = { ...currentFeature.symbol, visibleElements };
}
});
async function launchIdentify() {
if (!currentFeature) {
showToast({ level: 'warning', message: i18n('identify-advanced.missing-draw') });
return;
}
const selectedServices = Array.from(widgetState.servicesSelection.entries())
.filter(([, selection]) => selection.checked)
.map(([service]) => service)
.filter((service) => isQueryable(service));
if (!selectedServices.length) {
showToast({ level: 'warning', message: i18n('identify-advanced.missing-services') });
return;
}
const spatialFilter = currentBuffer ? geometryEngine.buffer(currentFeature, currentBuffer) : currentFeature;
const dataTableWidget = await widgetManager
.getReference<ServiceDataTableWidget>(config.dataTableWidgetId)
.activate();
dataTableWidget.openDataTables(
selectedServices.map((service) => ({
tabName: service.label,
service,
defaultSpatialFilter: spatialFilter,
disableAllFilters: true,
allowSelection: true,
additionalHeaderContent: [renderSnippet(headerContent, service)],
})),
);
}
function validateSelection(service: Queryable) {
const dataTableRef = widgetManager.getReference<ServiceDataTableWidget, ServiceDataTableState>(
config.dataTableWidgetId,
);
if (!dataTableRef) {
return;
}
const dataTableItemState = dataTableRef.state.get(service);
if (!dataTableItemState) {
return;
}
const featuresSelection = dataTableItemState.pageQuery.featuresSelection;
const unionFeature = geometryEngine.union(featuresSelection);
updateCurrentFeature(unionFeature);
zoom.zoomToFeature(unionFeature);
identifyAdvancedGraphicService.addFeature(unionFeature);
}
function updateCurrentFeature(feature: ApiFeature) {
if (currentFeature) {
identifyAdvancedGraphicService.removeFeature(currentFeature);
}
currentFeature = feature;
}
onDestroy(() => {
drawTool.destroy();
mapManager.removeMapService(serviceIdentifier);
});
</script>
{#snippet headerContent(service: Queryable)}
<Button
variant="outline"
class="gv-border-primary/70"
data-test-id="Identify-Advanced-Validate-Selection"
onclick={() => validateSelection(service)}
>
{i18n('identify-advanced.validate-selection')}
</Button>
{/snippet}
<div class="gv-flex gv-flex-col gv-gap-2 gv-p-2 gv-h-full">
<div class="gv-flex gv-flex-col gv-gap-2">
<p class="gv-text-lg gv-font-bold gv-pb-1 gv-border-b gv-border-muted-600">
{i18n('identify-advanced.geometry-title')}
</p>
<Tabs bind:value={activeDraw} class="gv-w-fit gv-m-auto">
<TabsList>
{#each identifyAdvancedDrawOptions as item (item.title)}
<TabsTrigger value={item.type} class="gv-gap-1.5 gv-font-bold gv-rounded-full">
<Icon icon={item.icon} class="gv-size-4" />
<span>{i18n(item.title)}</span>
</TabsTrigger>
{/each}
</TabsList>
</Tabs>
</div>
<div class="gv-flex gv-flex-col gv-gap-2">
<p class="gv-text-lg gv-font-bold gv-pb-1 gv-border-b gv-border-muted-600">
{i18n('identify-advanced.buffer-title')}
</p>
<div class="gv-w-full gv-flex gv-items-center">
<Label class="gv-w-full">{i18n('identify-advanced.buffer')}</Label>
<Input type="number" class="gv-w-full gv-h-7" min={0} bind:value={currentBuffer} />
</div>
</div>
<div class="gv-flex gv-flex-col gv-gap-2">
<p class="gv-text-lg gv-font-bold gv-pb-1 gv-border-b gv-border-muted-600">
{i18n('identify-advanced.layers-title')}
</p>
<div
class="gv-flex gv-flex-col gv-overflow-y-auto"
style={recordToCssStyle({ 'max-height': config.layerListMaxHeight })}
>
<Accordion multiple={true} value={serviceIds} class="gv-px-2">
{#each reverse(services) as service (service.id)}
<AccordionItem value={service.id}>
<AccordionTrigger>{service.label}</AccordionTrigger>
<AccordionContent>
<IdentifyAdvancedLayerSelect {service} {widgetState} displayLabel={false} />
</AccordionContent>
</AccordionItem>
{/each}
</Accordion>
</div>
</div>
<div class="gv-flex gv-justify-end">
<Button onclick={launchIdentify} data-test-id="IdentifyAdvanced-Launch" size="sm">Lancer</Button>
</div>
</div>

packages/common/src/lib/widgets/identify-advanced/IdentifyAdvancedLayerSelect.svelte

packages/common/src/lib/widgets/identify-advanced/IdentifyAdvancedLayerSelect.svelte
<script lang="ts">
import { hasSublayers, isSublayer } from '$lib/api/layers';
import { reverse } from '$lib/api/utils';
import { Checkbox } from '$lib/components/shadcn/ui/checkbox';
import { Label } from '$lib/components/shadcn/ui/label';
import SvelteSelf from './IdentifyAdvancedLayerSelect.svelte';
import { cn } from '$lib/components/shadcn/utils';
import type { ServiceOrLayer } from '$lib/widgets/identify-advanced/identify-advanced.model';
import type { IdentifyAdvancedState } from '$lib/widgets/identify-advanced/identify-advanced.state.svelte';
interface Props {
service: ServiceOrLayer;
widgetState: IdentifyAdvancedState;
displayLabel: boolean;
}
let { service, widgetState, displayLabel = true }: Props = $props();
if (isSublayer(service) && !widgetState.servicesSelection.has(service)) {
widgetState.add(service);
}
</script>
<div>
{#if hasSublayers(service)}
{#if displayLabel}
<Label>{service.label}</Label>
{/if}
<div class={cn('gv-flex gv-flex-col gv-gap-2 gv-mt-2', displayLabel ? 'gv-ml-5' : '')}>
{#each reverse(service.sublayers.list) as sublayer (sublayer.id)}
<SvelteSelf service={sublayer} {widgetState} displayLabel={true} />
{/each}
</div>
{:else if isSublayer(service)}
{@const serviceSelection = widgetState.servicesSelection.get(service)}
{#if serviceSelection}
<div
class="gv-flex gv-items-center gv-space-x-2"
data-test-id={`IdentifyAdvancedLayerSelect-${service.id}`}
>
<Checkbox
bind:checked={serviceSelection.checked}
data-test-id={`IdentifyAdvancedLayerSelect-${service.id}-Checkbox`}
/>
<Label>{service.label}</Label>
</div>
{/if}
{/if}
</div>

Aller plus loin