Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

2471-Comple_layer_tree_coverage #2473

Merged
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
72 changes: 51 additions & 21 deletions packages/geoview-core/public/templates/config-sandbox.html
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,8 @@ <h1><strong>Sandbox Configuration</strong></h1>
<option value="esriFeature">EsriFeature</option>
<option value="geoCore">GeoCore</option>
<option value="ogcWms">WMS</option>
<option value="esriImage">EsriImage</option>
<option value="GeoJSON">GeoJSON</option>
</select>
</div>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;URL:&nbsp;
Expand Down Expand Up @@ -318,11 +320,20 @@ <h1><strong>Sandbox Configuration</strong></h1>
<input type="checkbox" id="sampleUrlTab4" name="sampleUrlTab4">
</form>
GeoView Layer Type:
<select id="geoviewLayerTypeTab4">
<select id="geoviewLayerTypeTab4">
<option value="unknown">unknown</option>
<option value="esriDynamic">EsriDynamic</option>
<option value="esriFeature">EsriFeature</option>
<option value="ogcWms">WMS</option>
<option value="ogcWfs">WFS</option>
<option value="esriImage">EsriImage</option>
<option value="GeoJSON">GeoJSON</option>
<option value="CSV">CSV</option>
<option value="ogcFeature">Ogc Feature</option>
<option value="GeoPackage">GeoPackage</option>
<option value="xyzTiles">xyz Tiles</option>
<option value="vectorTiles">Vector Tiles</option>
<option value="imageStatic">Image Static</option>
</select>
</div>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;URL:&nbsp;
Expand Down Expand Up @@ -446,7 +457,7 @@ <h4 id="HLCONF1">Sanbox Map</h4>
generateLayerPathErrorFlag = (listOfLayerEntryConfig, buffer = '', prefix = '') => {
listOfLayerEntryConfig.forEach((layer) => {
buffer = buffer ? `${buffer}\n` : '';
buffer = `${buffer}${layer.getLayerPath()}: ${layer.getErrorDetectedFlag() ? 'ERROR' : 'Ok'}`;
buffer = `${buffer}${layer?.getLayerPath?.() || layer.layerName}: ${layer?.getErrorDetectedFlag?.() ? 'ERROR' : 'Ok'}`;
if (layer.isLayerGroup) buffer = generateLayerPathErrorFlag(layer.listOfLayerEntryConfig, buffer, prefix ? `${prefix}.${layer.layerId}` : layer.layerId);
});
return buffer;
Expand Down Expand Up @@ -656,6 +667,8 @@ <h4 id="HLCONF1">Sanbox Map</h4>
esriDynamic: 'https://maps-cartes.ec.gc.ca/arcgis/rest/services/CESI/MapServer/',
esriFeature: 'https://maps-cartes.services.geo.ca/server_serveur/rest/services/NRCan/Temporal_Test_Bed_en/MapServer/',
ogcWms: 'https://geo.weather.gc.ca/geomet',
esriImage: 'NOT IMPLEMENTED', // 'https://www5.agr.gc.ca/atlas/rest/services/imageservices/annual_crop_inventory_2022/ImageServer',
GeoJSON: 'NOT IMPLEMENTED', // 'https://canadian-geospatial-platform.github.io/geoview/public/datasets/geojson/metadata.json',
};

// Get the GeoView Layer Type drop-down object used to select the type of layer.
Expand Down Expand Up @@ -685,8 +698,8 @@ <h4 id="HLCONF1">Sanbox Map</h4>
// Type drop-down with the resulting value.
serviceUrlAreaTab2.addEventListener('keyup', (e) => {
if (sampleUrlTab2.checked) return;
GeoviewLayerTypeDropDownTab2.value = cgpv.api.config.guessLayerType(serviceUrlAreaTab2.value);
if (!GeoviewLayerTypeDropDownTab2.value) GeoviewLayerTypeDropDownTab2.value = 'unknown';
const value = cgpv.api.config.guessLayerType(serviceUrlAreaTab2.value);
GeoviewLayerTypeDropDownTab2.value = value || 'unknown';
});

// Initialise the GeoView Layer Type drop-down value and set input field accordingly
Expand Down Expand Up @@ -843,6 +856,15 @@ <h4 id="HLCONF1">Sanbox Map</h4>
esriDynamic: 'https://maps-cartes.ec.gc.ca/arcgis/rest/services/CESI/MapServer/',
esriFeature: 'https://maps-cartes.services.geo.ca/server_serveur/rest/services/NRCan/Temporal_Test_Bed_en/MapServer/',
ogcWms: 'https://geo.weather.gc.ca/geomet',
esriImage: 'https://www5.agr.gc.ca/atlas/rest/services/imageservices/annual_crop_inventory_2022/ImageServer',
GeoJSON: 'https://canadian-geospatial-platform.github.io/geoview/public/datasets/geojson/metadata.json',
CSV: 'https://canadian-geospatial-platform.github.io/geoview/public/datasets/csv-files/Station_List_Minus_HQ-MELCC.csv',
ogcFeature: 'https://b6ryuvakk5.execute-api.us-east-1.amazonaws.com/dev/collections',
GeoPackage: 'https://canadian-geospatial-platform.github.io/geoview/public/datasets/geopackages/rivers.gpkg',
ogcWfs: 'https://ahocevar.com/geoserver/wfs?REQUEST=GetCapabilities&VERSION=2.0.0&SERVICE=WFS',
xyzTiles: 'https://server.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer/tile/{z}/{y}/{x}',
vectorTiles: 'https://tiles.arcgis.com/tiles/HsjBaDykC1mjhXz9/arcgis/rest/services/CBMT3978_v11/VectorTileServer/tile/{z}/{y}/{x}.pbf',
imageStatic: 'https://datacube-prod-data-public.s3.ca-central-1.amazonaws.com/store/imagery/aerial/napl/napl-ring-of-fire/napl-ring-of-fire-1954-08-07-60k-thumbnail.png',
};

// Get the GeoView Layer Type drop-down object used to select the type of layer.
Expand Down Expand Up @@ -905,23 +927,31 @@ <h4 id="HLCONF1">Sanbox Map</h4>
return item.trim();
}) : [];

// Call the ConfigApi to get the Layer Tree
const metadataLayerTree = await cgpv.api.config.createMetadataLayerTree(
serviceUrlAreaTab4.value.trim(),
layerTypeTab4,
layerListTab4,
languageTab4
);
// Output instanciation result
metadataLayerTreeString = `${JSON.stringify(metadataLayerTree, (key, value) => {
if (['', 'layerId', 'layerName', 'isLayerGroup', 'listOfLayerEntryConfig'].includes(key)) return value;
if (/^\d+$/.test(key)) return value;
return undefined;
}, 2)}\n`;
layerPathErrorFlag = generateLayerPathErrorFlag(metadataLayerTree);

if (metadataLayerTree) printMessage(messageTab4, 'Layer tree is valid');
else printMessage(messageTab4, 'Cannot generate layer tree, see console for details...', 'error');
try {
// Call the ConfigApi to get the Layer Tree
const metadataLayerTree = await cgpv.api.config.createMetadataLayerTree(
serviceUrlAreaTab4.value.trim(),
layerTypeTab4,
layerListTab4,
languageTab4
);

// Output instanciation result
metadataLayerTreeString = `${JSON.stringify(metadataLayerTree, (key, value) => {
// Only display essential information
if (['', 'layerId', 'layerName', 'isLayerGroup', 'listOfLayerEntryConfig'].includes(key)) return value;
if (/^\d+$/.test(key)) return value;
return undefined;
}, 2)}\n`;
layerPathErrorFlag = generateLayerPathErrorFlag(metadataLayerTree);

printMessage(messageTab4, 'Layer tree is valid');
} catch (error) {
metadataLayerTreeString = '';
layerPathErrorFlag = '[]';
printMessage(messageTab4, 'Cannot generate layer tree, see console for details...', 'error');
cgpv.logger.logError('Cannot generate layer tree, see console for details\n', error);
}

elementSelectorTab4.dispatchEvent(new Event('change'));
});
Expand Down
125 changes: 121 additions & 4 deletions packages/geoview-core/src/api/config/config-api.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import cloneDeep from 'lodash/cloneDeep';
import mergeWith from 'lodash/mergeWith';

import { CV_DEFAULT_MAP_FEATURE_CONFIG, CV_CONFIG_GEOCORE_TYPE, CV_CONST_LAYER_TYPES } from '@config/types/config-constants';
import { TypeJsonValue, TypeJsonObject, toJsonObject, TypeJsonArray, Cast } from '@config/types/config-types';
Expand All @@ -12,7 +13,15 @@ import {
} from '@config/types/map-schema-types';
import { MapConfigError } from '@config/types/classes/config-exceptions';

import { generateId, isJsonString, removeCommentsFromJSON } from '@/core/utils/utilities';
import {
createLocalizedString,
findPropertyNameByRegex,
generateId,
getXMLHttpRequest,
isJsonString,
removeCommentsFromJSON,
xmlToJson,
} from '@/core/utils/utilities';
import { logger } from '@/core//utils/logger';

/**
Expand Down Expand Up @@ -486,7 +495,7 @@ export class ConfigApi {
geoviewLayerId: generateId(),
geoviewLayerName: { en: 'unknown', fr: 'inconnu' },
geoviewLayerType: layerType,
metadataAccessPath: { en: serviceAccessString, fr: serviceAccessString },
metadataAccessPath: createLocalizedString(serviceAccessString),
listOfLayerEntryConfig: listOfLayerId.map((layerId) => {
return { layerId };
}),
Expand Down Expand Up @@ -515,10 +524,118 @@ export class ConfigApi {
listOfLayerId: TypeJsonArray = [],
language: TypeDisplayLanguage = 'en'
): Promise<EntryConfigBaseClass[]> {
const geoviewLayerConfig = await ConfigApi.createLayerConfig(serviceAccessString, layerType, listOfLayerId, language);
// GV: TEMPORARY SECTION TO BE DELETED WHEN ALL LAYER TYPES ARE IMPLEMENTED
// GV: THE CODE IN THIS SECTION IS NOT PERMANANT BECAUSE THE CORRESPONDING
// GV: GEOVIEW LAYER CLASSES HAVE NOT YET BEEN IMPLEMENTED.
// GV: BEGINNING OF TEMPORARY SECTION
async function fetchJsonMetadata(url: string): Promise<TypeJsonObject> {
const response = await fetch(`${url}?f=json`);
return response.json();
}

async function fetchXmlMetadata(url: string): Promise<TypeJsonObject> {
let metadataUrl = url;
// check if url contains metadata parameters for the getCapabilities request and reformat the urls
const getCapabilitiesUrl =
metadataUrl!.indexOf('?') > -1 ? metadataUrl.substring(metadataUrl!.indexOf('?')) : `?service=WFS&request=GetCapabilities`;
metadataUrl = metadataUrl!.indexOf('?') > -1 ? metadataUrl.substring(0, metadataUrl!.indexOf('?')) : metadataUrl;
if (metadataUrl) {
const metadataString = await getXMLHttpRequest(`${metadataUrl}${getCapabilitiesUrl}`);
if (metadataString === '{}') throw new MapConfigError('Unable to build metadata layer tree (empty metadata).');
else {
// need to pass a xmldom to xmlToJson
const xmlDOMCapabilities = new DOMParser().parseFromString(metadataString, 'text/xml');
const xmlJsonCapabilities = xmlToJson(xmlDOMCapabilities);
const capabilitiesObject = findPropertyNameByRegex(xmlJsonCapabilities, /(?:WFS_Capabilities)/);
return capabilitiesObject as TypeJsonObject;
}
} else throw new MapConfigError('Unable to build metadata layer tree (empty metadata url).');
}

let jsonData: TypeJsonObject;
switch (layerType) {
case 'ogcWfs':
jsonData = (await fetchXmlMetadata(serviceAccessString))?.FeatureTypeList?.FeatureType;
if (Array.isArray(jsonData))
return (jsonData as TypeJsonArray).map((layer) => {
return Cast<EntryConfigBaseClass>({
layerId: layer.Name['#text'],
layerName: layer.Title['#text'],
});
});
return [];
break;
case 'ogcFeature':
jsonData = await fetchJsonMetadata(serviceAccessString);
if (jsonData.collections)
return (jsonData.collections as TypeJsonArray).map((layer) => {
return Cast<EntryConfigBaseClass>({
layerId: layer.id,
layerName: layer.title,
});
});
if (jsonData.id)
return [
Cast<EntryConfigBaseClass>({
layerId: jsonData.id,
layerName: jsonData.title,
}),
];
return [];
break;
case 'esriImage':
jsonData = await fetchJsonMetadata(serviceAccessString);
if (jsonData.name)
return [
Cast<EntryConfigBaseClass>({
layerId: jsonData.name,
layerName: jsonData.name,
}),
];
return [];
break;
case 'GeoJSON':
if (
serviceAccessString.toLowerCase().split('?')[0].endsWith('.json') ||
serviceAccessString.toLowerCase().split('?')[0].endsWith('.geojson')
) {
jsonData = await fetchJsonMetadata(serviceAccessString.split('?')[0]);
jsonData = mergeWith(jsonData, cloneDeep(jsonData), (property, sourceValue) => {
if (property.en || property.fr) return sourceValue[language] || sourceValue.en || sourceValue.fr;
return undefined;
});
return Cast<EntryConfigBaseClass[]>(jsonData.listOfLayerEntryConfig);
}
return [];
break;
case 'CSV':
case 'xyzTiles':
case 'imageStatic':
case 'vectorTiles':
case 'GeoPackage':
return [];
break;
default:
break;
}

// GV: END OF TEMPORARY SECTION

const geoviewLayerConfig = await ConfigApi.createLayerConfig(serviceAccessString, layerType, [], language);

if (geoviewLayerConfig && !geoviewLayerConfig.getErrorDetectedFlag()) {
// set layer tree creation filter (only the layerIds specified will be retained).
// If an empty Array [] is used, the layer tree will be built using the service metadata.
geoviewLayerConfig.setMetadataLayerTree(
Cast<EntryConfigBaseClass[]>(
listOfLayerId.map((layerId) => {
return { layerId };
})
)
);

await geoviewLayerConfig.fetchServiceMetadata();
if (!geoviewLayerConfig.getErrorDetectedFlag()) return geoviewLayerConfig.getMetadataLayerTree();
if (!geoviewLayerConfig.getErrorDetectedFlag()) return geoviewLayerConfig.getMetadataLayerTree()!;
}
throw new MapConfigError('Unable to build metadata layer tree.');
}
Expand Down
Loading
Loading