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
9 changes: 4 additions & 5 deletions apps/topicmaps/generic/public/dev/wc_karte/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,14 @@
"applicationMenuIntroductionMarkdown": "Die WC-Karte Wuppertal ist eine Anwendung der **GenericTopicMap Wuppertal**. Dies ist eine Komponente aus dem Gesamtsystem des Digitalen Zwillings der Stadt Wuppertal (**DigiTal Zwilling**), mit der eine einfache, für den mobilen Einsatz prädestinierte Web-Karte erzeugt werden kann, indem zwei Konfigurationsdateien angepasst werden. Über **Einstellungen** können Sie die Darstellung der Hintergrundkarte an Ihre Vorlieben anpassen. Wählen Sie **Kompaktanleitung** für detailliertere Bedienungsinformationen und **Urbaner Digitaler Zwilling** für eine Einordnung der WC-Karte in den Kontext des DigiTal Zwillings.",
"previewMapPosition": "lat=51.26041096180761&lng=7.175016403198243&zoom=13",
"applicationMenuSkipSymbolsizeSetting": false,

"filteringEnabled": true,
"vectorLayers": [
{
"name": "öffentliche Toiletten",
"layer": "poi_toiletten@https://maps.wuppertal.de/poi?service=WMS&request=GetCapabilities&version=1.1.1",
"addMetaInfoToHelp": true,
"layerType": "vector",
"style": "https://tiles.cismet.de/poi/offentliche-toiletten.style.json",
"opacity": 1
"style": "https://tiles.cismet.de/toiletten/style.json",
"opacity": 1,
"infoBoxMappingFunction":"@createInfoBoxInfo.js"
}
]
}
Expand Down
56 changes: 56 additions & 0 deletions apps/topicmaps/generic/public/dev/wc_karte/createInfoBoxInfo.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
function createInfoBoxInfo(p) {
const isToilette = p.carmaInfo && p.carmaInfo.sourceLayer === 'toiletten';
if (!isToilette) return null;
const c_Konfiguration = '';
const iconBaseUrl = 'https://tiles.cismet.de/toiletten/assets/icons/';
const fotoUrlPrefix = 'https://tiles.cismet.de/toiletten/assets/pics/';
const item = p;
const c_Foto_bauen = '';
let foto;
if (item.HAUPTBILD) {
foto = fotoUrlPrefix + item.HAUPTBILD.split('/').pop();
}
const c_Icon_Liste_aufbauen = '';
const icons = [];
const c_24_7 = '';
if (item['Q_24/7_OFF'] === 'ja') {
icons.push(iconBaseUrl + 'Infobox_24_7_Geoeffnet.svg');
}
const c_Entgelt = '';
if (item.ENTGELT === 'ja') {
icons.push(iconBaseUrl + 'Infobox_Kostenpflichtig.svg');
} else if (item.ENTGELT === 'nein') {
icons.push(iconBaseUrl + 'Infobox_Kostenfrei.svg');
}
const c_Rollstuhlgerecht = '';
if (item.ROLLGER === 'ja') {
icons.push(iconBaseUrl + 'Infobox_Rollstuhlgerecht.svg');
}
const c_Wickeltisch = '';
if (item.WICKELTIS === 'ja') {
icons.push(iconBaseUrl + 'Infobox_Wickeltisch.svg');
}
let barrHinwText = item.BARR_HINW || '';
const c_Subtitle_HTML = '';
let subtitleHtml = null;
if (icons.length > 0 || item.OEFFNUNGS || barrHinwText) {
let iconImgs = '';
for (let i = 0; i < icons.length; i++) {
iconImgs += '<img src=\"' + icons[i] + '\" style=\"height:24px; margin:6px 0; flex:0 0 auto;\"/>';
}
const opening = item.OEFFNUNGS ? '<div style=\"font-size:11px; margin-bottom:4px;\">' + item.OEFFNUNGS + '</div>' : '';
const iconsRow = icons.length > 0 ? '<div style=\"display:flex; gap:4px;\">' + iconImgs + '</div>' : '';
const barrHinw = barrHinwText ? '<div style=\"font-size:11px; margin-top:4px;\">' + barrHinwText + '</div>' : '';
subtitleHtml = '<html>' + '<div style=\"width:100%; margin-top:4px;\">' + opening + iconsRow + barrHinw + '</div>' + '</html>';
}
const additionalInfoHtml = '<html>' + (item.STRASSE || '') + (item.HAUSNUMMER ? ' ' + item.HAUSNUMMER : '') + '<br/><br/>' + (item.ORTSBESCHR || '') + '</html>';
const info = {
headerColor: '#4378CC',
header: 'Öffentliche Toiletten (' + item.NUTZUNG + ')',
title: item.NAME || 'Öffentliche Toilette',
additionalInfo: additionalInfoHtml,
subtitle: subtitleHtml,
foto: foto
};
return info;
}
7 changes: 7 additions & 0 deletions apps/topicmaps/generic/src/app/App.css
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,10 @@
transform: rotate(360deg);
}
}

/* Filter button responsive text hiding */
@media (max-width: 590px) {
.filter-button-text {
display: none;
}
}
61 changes: 61 additions & 0 deletions apps/topicmaps/generic/src/app/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,7 @@ function App({ name }) {
style: layer?.other?.vectorStyle,
infoboxMapping: layer?.conf?.infoboxMapping,
};

const styleVal = layer.conf?.vectorStyle;
if (styleVal && styleVal !== "") {
layerObj.style = styleVal;
Expand Down Expand Up @@ -521,6 +522,66 @@ function App({ name }) {
loadStyleManipulation(layer, configServer, configPath, slugName)
);
await Promise.all(manipulationPromises);

// --- InfoBox Mapping: Load mapping function from JS file if needed ---
function getInfoBoxMappingFunctionUrl(
infoBoxMappingFunction,
configServer,
configPath,
slugName
) {
if (
typeof infoBoxMappingFunction === "string" &&
infoBoxMappingFunction.startsWith("@")
) {
const filename = infoBoxMappingFunction.slice(1);
const path = configPath.endsWith("/")
? configPath
: configPath + "/";
const server = configServer.endsWith("/")
? configServer.slice(0, -1)
: configServer;
return `${server}${path}${slugName}/${filename}`;
}
return null;
}
async function loadInfoBoxMappingFunction(
layer,
configServer,
configPath,
slugName
) {
if (
layer.infoBoxMappingFunction &&
typeof layer.infoBoxMappingFunction === "string" &&
layer.infoBoxMappingFunction.startsWith("@") &&
!layer.infoboxMapping
) {
const url = getInfoBoxMappingFunctionUrl(
layer.infoBoxMappingFunction,
configServer,
configPath,
slugName
);
try {
const code = await fetch(url).then((r) => r.text());
// The infoboxMapping expects an array with the function as a string
// Remove newlines to avoid syntax errors in sandboxed eval
const singleLineCode = code.replace(/\n/g, " ");
layer.infoboxMapping = [singleLineCode];
} catch (e) {
log(
`Failed to fetch/parse infoBoxMappingFunction for layer ${
layer.name || layer.id
}: ${e}`
);
}
}
}
const infoBoxMappingPromises = config.tm.vectorLayers.map((layer) =>
loadInfoBoxMappingFunction(layer, configServer, configPath, slugName)
);
await Promise.all(infoBoxMappingPromises);
}

// Normalize layers: if only 'layer' is present, extract 'capabilitiesLayer' and 'capabilities'
Expand Down
31 changes: 27 additions & 4 deletions apps/topicmaps/generic/src/app/Map.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import {
} from "@carma-appframeworks/portals";
import { EmptySearchComponent } from "@carma-mapping/fuzzy-search";
import FuzzySearchWrapper from "./components/FuzzySearchWrapper";
import { FilterButtons } from "./components/FilterButtons";
import { Control, ControlLayout } from "@carma-mapping/map-controls-layout";
import {
FullscreenControl,
Expand Down Expand Up @@ -89,7 +90,8 @@ function renderCismapLayers(
markerSymbolSize,
setGlobalHits,
initialVisualSelection,
layerInformation
layerInformation,
setMaplibreMap
) {
return (
<>
Expand Down Expand Up @@ -128,6 +130,11 @@ function renderCismapLayers(
return ret;
});
}}
onMapLibreCoreMapReady={(map) => {
if (setMaplibreMap) {
setMaplibreMap(map);
}
}}
/>
);
})}
Expand All @@ -149,13 +156,17 @@ const Map = ({
const [cl_key, setClKey] = useState("");
const { routedMapRef } = useSelectionTopicMap() ?? {};
const [selectedVectorObject, setSelectedVectorObject] = useState(undefined);
const [maplibreMap, setMaplibreMap] = useState(null);
// console.log("xxx markerSymbolSize", markerSymbolSize);

// lets assume we will only have vector layers
useEffect(() => {
const getFeature = async (infoboxMapping, hit) => {
// console.log("xxx infoboxMapping", infoboxMapping);
console.log("xxx infoboxMapping", infoboxMapping);
const feature = await createVectorFeature(infoboxMapping, hit);

// console.log("xxx a2c setFeature", feature);

setFeature(feature);
};

Expand Down Expand Up @@ -269,6 +280,16 @@ const Map = ({
</Control>
)}

{config?.tm?.filteringEnabled && config?.tm?.vectorLayers && (
<Control position="topcenter" order={10}>
<FilterButtons
maplibreMap={maplibreMap}
selectedFeature={feature}
setSelectedFeature={setFeature}
/>
</Control>
)}

<SecondaryInfoModal
feature={selectedFeature}
footer={
Expand Down Expand Up @@ -316,7 +337,8 @@ const Map = ({
markerSymbolSize,
setGlobalHits,
selectedVectorObject,
layerInformation
layerInformation,
null
)}
previewFeatureCollectionCount={
config?.tm?.previewFeatureCollectionCount
Expand Down Expand Up @@ -348,7 +370,8 @@ const Map = ({
markerSymbolSize,
setGlobalHits,
selectedVectorObject,
layerInformation
layerInformation,
setMaplibreMap
)}
{config.tm.noFeatureCollection !== true && (
<>
Expand Down
Loading
Loading