diff --git a/assets/button-stepped-border-image.svg b/assets/button-stepped-border-image.svg deleted file mode 100644 index 229a1aa..0000000 --- a/assets/button-stepped-border-image.svg +++ /dev/null @@ -1,66 +0,0 @@ - - - - diff --git a/assets/neon-border-image.svg b/assets/neon-border-image.svg deleted file mode 100644 index b78b240..0000000 --- a/assets/neon-border-image.svg +++ /dev/null @@ -1,69 +0,0 @@ - - - - diff --git a/couches/autogestion.geojson b/couches/autogestion.geojson index cba8f7f..b53cc2c 100644 --- a/couches/autogestion.geojson +++ b/couches/autogestion.geojson @@ -4,6 +4,7 @@ "crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:OGC:1.3:CRS84" } }, "features": [ { "type": "Feature", "properties": { "name": "Tableau de programmation" }, "geometry": { "type": "Point", "coordinates": [ -2.091304219258237, 47.541608316100941 ] } }, -{ "type": "Feature", "properties": { "name": "Tableau des shifts" }, "geometry": { "type": "Point", "coordinates": [ -2.091287983083753, 47.541680943841193 ] } } +{ "type": "Feature", "properties": { "name": "Tableau des shifts" }, "geometry": { "type": "Point", "coordinates": [ -2.091287266713699, 47.541705450550154 ] } }, +{ "type": "Feature", "properties": { "name": "Tableau des shifts" }, "geometry": null } ] } diff --git a/couches/batiments.geojson b/couches/batiments.geojson index 0dd01a7..86550a4 100644 --- a/couches/batiments.geojson +++ b/couches/batiments.geojson @@ -3,8 +3,8 @@ "name": "batiments", "crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:OGC:1.3:CRS84" } }, "features": [ -{ "type": "Feature", "properties": { "id": 1, "nom": "Batiment A", "batiment": true }, "geometry": { "type": "MultiPolygon", "coordinates": [ [ [ [ -2.091581996371882, 47.541032945940522 ], [ -2.09151399426637, 47.541497984433285 ], [ -2.091312011285422, 47.541481979469303 ], [ -2.091326034403365, 47.541400730704773 ], [ -2.091241014242576, 47.541394942672135 ], [ -2.091251030755392, 47.541319968658911 ], [ -2.091334848934643, 47.541327974547109 ], [ -2.091346067428997, 47.541254515067628 ], [ -2.091304879528295, 47.541251918559851 ], [ -2.091309046397626, 47.541206046901102 ], [ -2.091353038921917, 47.541207940189693 ], [ -2.091375035184063, 47.541061913344244 ], [ -2.091330882395567, 47.541059965956265 ], [ -2.091335990817103, 47.541026008366721 ], [ -2.091380744596368, 47.54102704967903 ], [ -2.091381966610932, 47.541019003174263 ], [ -2.091581996371882, 47.541032945940522 ] ] ] ] } }, -{ "type": "Feature", "properties": { "id": 2, "nom": "Batiment B", "batiment": true, "electricite-triphase": true }, "geometry": { "type": "MultiPolygon", "coordinates": [ [ [ [ -2.091630987136071, 47.541689935433055 ], [ -2.091803321239079, 47.54170457447416 ], [ -2.091796950736927, 47.541739951576218 ], [ -2.092355992350255, 47.541780954364874 ], [ -2.092339965929748, 47.54185403445365 ], [ -2.091958937782201, 47.541822011214833 ], [ -2.09193297498098, 47.54193998862457 ], [ -2.091763976376737, 47.541926005529561 ], [ -2.091756003232534, 47.541964993201951 ], [ -2.091590991200393, 47.541954999486393 ], [ -2.091630987136071, 47.541689935433055 ] ] ] ] } }, -{ "type": "Feature", "properties": { "id": 10, "nom": "Toilettes sèches", "batiment": true }, "geometry": { "type": "MultiPolygon", "coordinates": [ [ [ [ -2.091342607190285, 47.541484412003662 ], [ -2.091438001950184, 47.541492274863295 ], [ -2.09143324403292, 47.541516949622384 ], [ -2.09133755253389, 47.541508679404679 ], [ -2.091342607190285, 47.541484412003662 ] ] ] ] } } +{ "type": "Feature", "properties": { "id": 1, "nom": "Batiment A" }, "geometry": { "type": "MultiPolygon", "coordinates": [ [ [ [ -2.091581996371882, 47.541032945940522 ], [ -2.09151399426637, 47.541497984433285 ], [ -2.091312011285422, 47.541481979469303 ], [ -2.091326034403365, 47.541400730704773 ], [ -2.091241014242576, 47.541394942672135 ], [ -2.091251030755392, 47.541319968658911 ], [ -2.091334848934643, 47.541327974547109 ], [ -2.091346067428997, 47.541254515067628 ], [ -2.091304879528295, 47.541251918559851 ], [ -2.091309046397626, 47.541206046901102 ], [ -2.091353038921917, 47.541207940189693 ], [ -2.091375035184063, 47.541061913344244 ], [ -2.091330882395567, 47.541059965956265 ], [ -2.091335990817103, 47.541026008366721 ], [ -2.091380744596368, 47.54102704967903 ], [ -2.091381966610932, 47.541019003174263 ], [ -2.091581996371882, 47.541032945940522 ] ] ] ] } }, +{ "type": "Feature", "properties": { "id": 2, "nom": "Batiment B", "electricite-triphase": true }, "geometry": { "type": "MultiPolygon", "coordinates": [ [ [ [ -2.091630987136071, 47.541689935433055 ], [ -2.091803321239079, 47.54170457447416 ], [ -2.091796950736927, 47.541739951576218 ], [ -2.092355992350255, 47.541780954364874 ], [ -2.092339965929748, 47.54185403445365 ], [ -2.091958937782201, 47.541822011214833 ], [ -2.09193297498098, 47.54193998862457 ], [ -2.091763976376737, 47.541926005529561 ], [ -2.091756003232534, 47.541964993201951 ], [ -2.091590991200393, 47.541954999486393 ], [ -2.091630987136071, 47.541689935433055 ] ] ] ] } }, +{ "type": "Feature", "properties": { "id": 10, "nom": "Toilettes sèches" }, "geometry": { "type": "MultiPolygon", "coordinates": [ [ [ [ -2.091342607190285, 47.541484412003662 ], [ -2.091438001950184, 47.541492274863295 ], [ -2.09143324403292, 47.541516949622384 ], [ -2.09133755253389, 47.541508679404679 ], [ -2.091342607190285, 47.541484412003662 ] ] ] ] } } ] } diff --git a/couches/medic.geojson b/couches/medic.geojson index bfbff26..71a1334 100644 --- a/couches/medic.geojson +++ b/couches/medic.geojson @@ -3,7 +3,9 @@ "name": "medic", "crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:OGC:1.3:CRS84" } }, "features": [ -{ "type": "Feature", "properties": { "name": "infirmerie" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -2.091983279274473, 47.541750296814556 ], [ -2.092011290414653, 47.541752703414282 ], [ -2.092017401936146, 47.541713510204964 ], [ -2.091990409382882, 47.54171144740365 ], [ -2.091983279274473, 47.541750296814556 ] ] ] } }, +{ "type": "Feature", "properties": { "name": "Infirmerie" }, "geometry": null }, +{ "type": "Feature", "properties": { "name": "infirmerie" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -2.091966260276902, 47.541709299003671 ], [ -2.092019639165452, 47.541714403762832 ], [ -2.092031204591305, 47.541653747180767 ], [ -2.091978270526826, 47.54165014381725 ], [ -2.091966260276902, 47.541709299003671 ] ] ] } }, +{ "type": "Feature", "properties": { "name": "infirmerie" }, "geometry": null }, { "type": "Feature", "properties": { "name": "zone infirmerie" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -2.091842510161106, 47.54181195296588 ], [ -2.091847583778531, 47.541782685155603 ], [ -2.091792696462745, 47.541778014758826 ], [ -2.091786239131476, 47.54180697121209 ], [ -2.091842510161106, 47.54181195296588 ] ] ] } } ] } diff --git a/couches/piscine.geojson b/couches/piscine.geojson index 924ca7d..291a8ea 100644 --- a/couches/piscine.geojson +++ b/couches/piscine.geojson @@ -3,6 +3,6 @@ "name": "piscine", "crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:OGC:1.3:CRS84" } }, "features": [ -{ "type": "Feature", "properties": { "id": null, "name": "Piscine", "piscine": true }, "geometry": { "type": "MultiPolygon", "coordinates": [ [ [ [ -2.091707042517393, 47.542115661078718 ], [ -2.091903846960937, 47.542128643402442 ], [ -2.091884615256356, 47.542248080629903 ], [ -2.091687810812811, 47.542235963822122 ], [ -2.091707042517393, 47.542115661078718 ] ] ] ] } } +{ "type": "Feature", "properties": { "id": null, "name": "Piscine" }, "geometry": { "type": "MultiPolygon", "coordinates": [ [ [ [ -2.091707042517393, 47.542115661078718 ], [ -2.091903846960937, 47.542128643402442 ], [ -2.091884615256356, 47.542248080629903 ], [ -2.091687810812811, 47.542235963822122 ], [ -2.091707042517393, 47.542115661078718 ] ] ] ] } } ] } diff --git a/couches/wifi.geojson b/couches/wifi.geojson index 8476f2f..ea0ef34 100644 --- a/couches/wifi.geojson +++ b/couches/wifi.geojson @@ -3,8 +3,8 @@ "name": "wifi", "crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:OGC:1.3:CRS84" } }, "features": [ -{ "type": "Feature", "properties": { "wifi": true, "fixe": true }, "geometry": { "type": "Point", "coordinates": [ -2.091875157168781, 47.541822388950891 ] } }, -{ "type": "Feature", "properties": { "wifi": true, "fixe": true }, "geometry": { "type": "Point", "coordinates": [ -2.091771851987061, 47.541790332713326 ] } }, -{ "type": "Feature", "properties": { "wifi": true, "fixe": true }, "geometry": { "type": "Point", "coordinates": [ -2.092165078162641, 47.541781334467679 ] } } +{ "type": "Feature", "properties": { "fixe": true }, "geometry": { "type": "Point", "coordinates": [ -2.091875157168781, 47.541822388950891 ] } }, +{ "type": "Feature", "properties": { "fixe": true }, "geometry": { "type": "Point", "coordinates": [ -2.091771851987061, 47.541790332713326 ] } }, +{ "type": "Feature", "properties": { "fixe": true }, "geometry": { "type": "Point", "coordinates": [ -2.092165078162641, 47.541781334467679 ] } } ] } diff --git a/css/style.css b/css/style.css index 7e65655..cba1aa4 100644 --- a/css/style.css +++ b/css/style.css @@ -5,29 +5,12 @@ body, html { body { pointer-events: none; - - display: flex; - flex-direction: column; - justify-content: flex-start; - align-items: stretch; - - min-height: 100vh; } body > * { pointer-events: all; } -body > hr { - border: none; - padding: 0; - margin: 0; - flex: 1; - min-height: 50vh; - flex-shrink: 0; - pointer-events: none; -} - /* HEADER and NAV */ #main-header { @@ -88,8 +71,22 @@ body > hr { } } -.highlight-point-icon > * { - transform: translateX(-50%) translateY(-50%); +.map-hilight-area.active { + stroke: #ff4e00; + stroke-width: 5px; + fill: rgba(255, 77, 0, 0.25); +} + +.highlight-point-icon::after { + content: ""; + width: 5px; + height: 5px; + border-radius: 100%; + background: transparent; +} + +.active .highlight-point-icon::after { + background: #ff4e00; } .map-amenity-icon .map-amenity-icon-container { @@ -129,146 +126,4 @@ body > hr { filter: drop-shadow(0 0 10px black); opacity: clamp(0, calc( 1 - ( var(--zoom-level) - 18 ) ), 1) ; pointer-events: none; -} - -/* SAERCH FORM */ - -#search-section { - border: solid 10px black; - border-image: url("../assets/button-stepped-border-image.svg") 16 15 fill; - z-index: 100; - margin-bottom: 5px; - margin-left: 5px; - width: fit-content; - max-width: 100%; - background: black; - background-clip: content-box; - color: white; - - display: grid; - grid-template-columns: min-content 1fr; - grid-template-rows: 1fr; -} - -#search-section input { - background: black; - font-size: 1em; - color: inherit; - font-family: monospace; - border: none; - padding: 1ex; - - grid-column: 2; - grid-row: 1; -} - -#search-section button { - background: transparent; - border: none; - padding: 1ex; - grid-column: 1; - grid-row: 1; - cursor: pointer; -} - -#search-section label { - grid-column: 2; - grid-row: 1; - padding: 1ex; - align-self: flex-end; -} - -#search-section button img { - height: 1.5em; -} - -/* PANEL */ - -#result-panel { - background: black; - border-image: url("../assets/neon-border-image.svg") 12; - border-top: 5.5px white solid; - - color: white; -} - -#result-panel.empty, -#result-panel:empty{ - display:none; -} - -#result-panel > * { - padding: 1em; - margin: 0; -} - -#result-panel::part(navigation-panel) { - background: black; - color: white; - box-shadow: 0 0 10px black; - border-top: solid 1px rgba(255, 255, 255, 0.5); -} - -#result-panel::part(dot), -#result-panel::part(dot-active) { - border-radius: 0; - width: 1ex; - height: 1ex; - margin-top: 0.5ex; -} - -#result-panel::part(dot-active) { - transform-origin: 0% 100%; - transform: scaleX(1) scaleY(1.5); -} - -camp-feature-short-header { - display: grid; - grid-template-rows: 1fr min-content; - grid-template-columns: 3em 1fr; - gap: 0.5ex 0.5em; - vertical-align: center; -} - -camp-feature-short-header .feature-legend { - grid-row: 1 / 3; - height: 3em; - align-self: flex-start; -} - -camp-feature-short-header h2 { - margin: 0; - padding: 0; - font-size: 1em; - font-weight: bold; -} - -camp-feature-short-header .feature-location { - margin: 0; - padding: 0; - font-size: 0.6em; - opacity: 0.75; -} - -#search-result { - list-style-type: none; -} - -#search-result > li:not(:first-child) { - margin-top: 1em; -} - -#search-result > li:not(:last-child) { - margin-bottom: 1em; -} - -#search-result > li > a { - text-decoration: none; - color: inherit; -} - -/* FEATURE */ - -camp-feature { - min-height: 25vh; -} +} \ No newline at end of file diff --git a/icons/arrow-left.svg b/icons/arrow-left.svg deleted file mode 100644 index 666b191..0000000 --- a/icons/arrow-left.svg +++ /dev/null @@ -1,19 +0,0 @@ - - - - diff --git a/icons/arrow-right.svg b/icons/arrow-right.svg deleted file mode 100644 index 51a6708..0000000 --- a/icons/arrow-right.svg +++ /dev/null @@ -1,19 +0,0 @@ - - - - diff --git a/icons/default-marker.svg b/icons/default-marker.svg deleted file mode 100644 index ffe0405..0000000 --- a/icons/default-marker.svg +++ /dev/null @@ -1,48 +0,0 @@ - - - - - - - - - - diff --git a/icons/parking.svg b/icons/parking.svg deleted file mode 100644 index ec261d4..0000000 --- a/icons/parking.svg +++ /dev/null @@ -1,66 +0,0 @@ - - - -P diff --git a/icons/piscine.svg b/icons/piscine.svg deleted file mode 100644 index ecff3e1..0000000 --- a/icons/piscine.svg +++ /dev/null @@ -1,71 +0,0 @@ - - - - diff --git a/icons/search.svg b/icons/search.svg deleted file mode 100644 index 535d2ef..0000000 --- a/icons/search.svg +++ /dev/null @@ -1,541 +0,0 @@ - - - -Piscine++++++Piscine de l'AntenneAu centre de la pelouse d'entree sur le site, a cote des jeux pour enfantGrand atelier de nage libre, tous cyborg accepteEVENEMENTS A VENIRNage libre3 Juil17:00-19:00 diff --git a/icons/wifi.svg b/icons/wifi.svg deleted file mode 100644 index 7d02eae..0000000 --- a/icons/wifi.svg +++ /dev/null @@ -1,71 +0,0 @@ - - - - diff --git a/index.html b/index.html index 7163235..32e8aab 100644 --- a/index.html +++ b/index.html @@ -7,7 +7,6 @@ -
@@ -15,17 +14,26 @@
- -
- -
-
- - -
+ + + +
+
+
+ + +
+ +
- - + \ No newline at end of file diff --git a/js/components/bidi-panel.js b/js/components/bidi-panel.js deleted file mode 100644 index 73e09e3..0000000 --- a/js/components/bidi-panel.js +++ /dev/null @@ -1,274 +0,0 @@ -const TEMPLATE = document.createElement("template"); -TEMPLATE.innerHTML = ` - - -
- - -` - -export class BidiPanelElement extends HTMLElement { - - shadow - - mutationObserver - - #lastActive - #currentIndex - - get contentContainer(){ - return this.shadow.getElementById("content") - } - - get progressBar(){ - return this.shadow.getElementById("panel-progress-bar") - } - - get progressDotsContainer(){ - return this.shadow.getElementById("progress-dots") - } - - get activeChildrenIndex(){ - let thisGeometry = this.getBoundingClientRect() - let progress = this.contentContainer.scrollLeft / (this.contentContainer.scrollWidth - thisGeometry.width) - let focusedElementIndex = Math.floor(progress * this.children.length) - if(Number.isFinite(focusedElementIndex)){ - if(focusedElementIndex == this.children.length){ - return this.children.length - 1 - } else { - return focusedElementIndex - } - } else { - return null - } - } - - set activeChildrenIndex(newIndex){ - this.setActiveChildrenIndex(newIndex, {behavior: "auto"}) - } - - setActiveChildrenIndex(newIndex, options){ - let newPanel = this.children[newIndex] - let newPanelRect = newPanel.getBoundingClientRect() - let selfRect = this.getBoundingClientRect() - this.contentContainer.scrollTo({ - left: this.contentContainer.scrollLeft + ( newPanelRect.left - selfRect.left ), - behavior: options?.behavior || "auto" - }) - this.#currentIndex = newIndex - } - - get activeChildren(){ - return this.children[this.activeChildrenIndex] - } - - connectedCallback(){ - if(!this.shadow){ - this.shadow = this.attachShadow({ mode: "open" }) - this.shadow.append(TEMPLATE.content.cloneNode(true)) - } - this.contentContainer.addEventListener("scroll", this.handleContentScroll.bind(this)) - this.shadow.getElementById("nav-left-panel").addEventListener("click", () => this.previous()); - this.shadow.getElementById("nav-right-panel").addEventListener("click", () => this.next()); - - this.mutationObserver = (new MutationObserver(this.handleMutations.bind(this))) - this.mutationObserver.observe(this, {childList:true}) - - this.updateProgress() - } - - handleMutations(mutation){ - let newIndex = this.#currentIndex - this.updateProgress() - if(Number.isFinite(newIndex)){ - this.setActiveChildrenIndex(newIndex, {behavior: "instant"}) - } - this.requestDispatchChangeEvent() - } - - handleContentScroll(e){ - this.updateProgress() - this.#currentIndex = this.activeChildrenIndex - this.requestDispatchChangeEvent() - } - - requestDispatchChangeEvent(){ - let currentPanel = this.activeChildren - if(currentPanel != this.#lastActive){ - this.dispatchEvent(new ActivePanelChangeEvent("activePanelChange", this.#currentIndex)) - this.#lastActive = currentPanel - } - } - - updateProgress(){ - let focusedElementIndex = this.activeChildrenIndex; - - let progressBar = this.progressBar; - progressBar.value = focusedElementIndex; - progressBar.min = 0; - progressBar.max = this.children.length-1; - - this.classList.toggle("single-panel", this.children.length == 1) - this.classList.toggle("empty", this.children.length == 0) - - let dotsContainer = this.progressDotsContainer; - if(dotsContainer.children.length != parseInt(progressBar.max)+1){ - let dots = (new Array(this.children.length)) - .fill(0) - .map(() => { - let el = document.createElement("div") - el.classList.add("dot") - el.part = "dot" - return el - }); - dotsContainer.replaceChildren(...dots) - } - for(let inactiveDot of dotsContainer.querySelectorAll(".active")){ - inactiveDot.part = "dot" - inactiveDot.classList.remove("active") - } - - let activeDot = dotsContainer.children[parseInt(progressBar.value)] - if(activeDot){ - activeDot.part = "dot-active" - activeDot.classList.add("active") - } - } - - next(){ - let nextItem = this.activeChildrenIndex + 1; - if(nextItem < this.children.length){ - this.activeChildrenIndex = nextItem - } - } - - previous(){ - let nextItem = this.activeChildrenIndex - 1; - if(nextItem >= 0){ - this.activeChildrenIndex = nextItem - } - } -} - -export class ActivePanelChangeEvent extends Event { - constructor(name, activePanelIndex){ - super(name) - this.activePanelIndex = activePanelIndex - } -} - -customElements.define("camp-bidi-panel", BidiPanelElement); diff --git a/js/components/feature-legend.js b/js/components/feature-legend.js deleted file mode 100644 index 03ddb78..0000000 --- a/js/components/feature-legend.js +++ /dev/null @@ -1,72 +0,0 @@ -const TEMPLATE = document.createElement("template") -TEMPLATE.innerHTML = ` - -
- -
-` - -export class FeatureLegendElement extends HTMLElement { - - feature - - connectedCallback(){ - if(!this.shadow){ - this.shadow = this.attachShadow({mode: "open"}) - this.shadow.append(TEMPLATE.content.cloneNode(true)) - } - this.updateContent() - } - - updateContent(){ - let container = this.shadow.getElementById("legend-container") - let marker = this.shadow.getElementById("marker-icon") - container.hidden = !this.feature - if(this.feature){ - let symbol = this.feature.mapSymbol - container.style.backgroundColor = symbol.backgroundColor || "transparent" - container.style.backgroundImage = symbol.backgroundUrl ? `url(${symbol.backgroundUrl})` : "none" - container.style.borderColor = symbol.borderColor || "transparent" - if(symbol.markerUrl){ - marker.src = symbol.markerUrl - } else { - marker.removeAttribute("src") - } - } - } - -} - -customElements.define("camp-feature-legend", FeatureLegendElement) diff --git a/js/components/feature-short-header.js b/js/components/feature-short-header.js deleted file mode 100644 index e40adec..0000000 --- a/js/components/feature-short-header.js +++ /dev/null @@ -1,54 +0,0 @@ -import "./feature-legend.js" - -const TEMPLATE = document.createElement("template") -TEMPLATE.innerHTML = ` - -

- -` - -export class FeatureShortHeaderElement extends HTMLElement { - - #feature - - get feature(){ - return this.#feature - } - - set feature(value){ - this.#feature = value - let legend = this.querySelector(".feature-legend") - if(legend) - legend.feature = value - } - - connectedCallback(){ - this.updateContent() - } - - updateContent(){ - if(!this.feature){ - this.replaceChildren() - } else { - - this.replaceChildren(TEMPLATE.content.cloneNode(true)) - - this.querySelector(".feature-legend").feature = this.feature - this.querySelector(".feature-legend").updateContent() - - this.querySelector(".feature-name").textContent = - this.feature?.properties?.name || - this.feature.mapSymbol.genericName || - this.feature.id - - let featurePoint = this.feature.asPoint() - - this.querySelector(".feature-location").textContent = - this.feature?.properties?.["location-description"] || - `${featurePoint.geometry.coordinates[1]}, ${featurePoint.geometry.coordinates[0]}` - } - } - -} - -customElements.define("camp-feature-short-header", FeatureShortHeaderElement) diff --git a/js/components/feature.js b/js/components/feature.js deleted file mode 100644 index a8fb046..0000000 --- a/js/components/feature.js +++ /dev/null @@ -1,26 +0,0 @@ -import "./feature-short-header.js" - -const TEMPLATE = document.createElement("template") -TEMPLATE.innerHTML = ` - -` - -export class FeatureElement extends HTMLElement { - - feature - - connectedCallback(){ - this.updateContent() - } - - updateContent(){ - this.replaceChildren(TEMPLATE.content.cloneNode(true)) - - let header = this.querySelector("camp-feature-short-header") - header.feature = this.feature - header.updateContent() - } - -} - -customElements.define("camp-feature", FeatureElement) diff --git a/js/index.js b/js/index.js index e8fcae3..2bff483 100644 --- a/js/index.js +++ b/js/index.js @@ -1,7 +1,5 @@ import * as MAP from "./map.js" import { PlaceDatabase } from "./places.js"; -import {FeatureElement} from "./components/feature.js" -import "./components/feature-short-header.js" /** La carte */ @@ -21,7 +19,7 @@ window.interhackPlaces = places; await MAP.init_places(places) // GESTION de la recherche -const search_form = document.getElementById("search-section") +const search_form = document.getElementById("search-area") search_form.elements["query"].addEventListener("change", () => search_form.requestSubmit()) search_form.elements["query"].addEventListener("input", () => search_form.requestSubmit()) @@ -49,86 +47,18 @@ search_form.addEventListener("submit", e => { let a = document.createElement("a") el.append(a) a.href = "#"+encodeURIComponent(result_item.ref) - a.addEventListener("click", e => { - e.preventDefault() - openSearchResultItem(result_item.feature) - }) - let header = document.createElement("camp-feature-short-header") - header.feature = result_item.feature - a.append(header) + a.textContent = result_item.feature.properties.name+" ("+result_item.ref+")" resultElements.push(el) } - if(!document.getElementById("search-result")){ - let el = document.createElement("ol") - el.id = "search-result" - document.getElementById("result-panel").replaceChildren(el) - } - if(resultElements.length > 0){ document.getElementById("search-result").replaceChildren(...resultElements) } else { document.getElementById("search-result").replaceChildren(document.createTextNode("Pas de resultat")) } - - document.getElementById("search-result").children[0]?.scrollIntoView() } else { - document.getElementById("search-result")?.remove() + document.getElementById("search-result").replaceChildren([]) } } -}) - -function openSearchResultItem(feature){ - let searchResultContainer = document.getElementById("search-result") - if(searchResultContainer){ - let featureIndex = -1 - let featureFound = false - let newChildren = [] - for(let el of searchResultContainer.children){ - let featureHeader = el.querySelector("camp-feature-short-header") - if(!featureHeader) - continue - let root = document.createElement("camp-feature") - root.feature = featureHeader.feature - newChildren.push(root) - if(!featureFound){ - featureIndex += 1 - if(feature == root.feature){ - featureFound = true - } - } - } - let panel = document.getElementById("result-panel") - panel.replaceChildren(...newChildren) - requestAnimationFrame(() => { - if(featureFound){ - panel.setActiveChildrenIndex(featureIndex, {behavior: "instant"}) - } else { - panel.activeChildrenIndex = 0 - } - }) - updateActiveFeature(feature) - } -} - -function updateActiveFeature(feature_or_featureid){ - let newUrl = new URL(window.location) - if(feature_or_featureid){ - newUrl.hash = encodeURIComponent(feature_or_featureid.id || feature_or_featureid) - } else { - delete newUrl.hash; - } - window.history.replaceState(null, "", newUrl.toString()) - window.dispatchEvent(new Event("hashchange")) -} - -document.getElementById("result-panel").addEventListener("activePanelChange", e => { - let activeElement = e.target.children[e.activePanelIndex] - if(activeElement instanceof FeatureElement){ - let feature = activeElement.feature - updateActiveFeature(feature) - } else { - MAP.unhighlight_all() - } -}) +}) \ No newline at end of file diff --git a/js/map.js b/js/map.js index 439048d..c8a18f9 100644 --- a/js/map.js +++ b/js/map.js @@ -1,5 +1,5 @@ import * as L from "./lib/leaflet/leaflet-src.esm.js" -import { MapSubFeature, PlaceDatabase } from "./places.js"; +import { PlaceDatabase } from "./places.js"; import "./lib/turf/turf.js" let map = null; @@ -93,43 +93,93 @@ export async function init(){ const HIGHLIGHT_LAYER = Symbol("Highlight map layer") const AMENITY_MARKER_LAYER = Symbol("Amenity marker layer") const PARENT_FEATURE = Symbol("Parent feature") +const HIGHLIGHT_MARKER_ICON = L.divIcon({ + className: "highlight-point-icon" +}) export async function init_places(places_db){ places = places_db + + // La couche des zones disponibles + const amenities = L.geoJSON(null, { + pointToLayer: function(feature, latlng) { + let iconEl = document.createElement("div") + iconEl.classList.add("map-amenity-icon-container") + + if(feature.properties["eau potable"]){ + let drinking_water_icon = document.createElement("img") + drinking_water_icon.src = new URL("../icons/eau-potable.svg", import.meta.url).toString() + drinking_water_icon.alt = "Icone d'une goute" + drinking_water_icon.title = "Eau potable" + drinking_water_icon.classList.add("drinking-water") + iconEl.append(drinking_water_icon) + } + + if(feature.properties["toilettes"]){ + let bathrooms_icon = document.createElement("img") + bathrooms_icon.src = new URL("../icons/toilettes.svg", import.meta.url).toString() + bathrooms_icon.alt = "Icone de toilettes" + bathrooms_icon.title = "Toilettes" + bathrooms_icon.classList.add("bathroom") + iconEl.append(bathrooms_icon) + } + + if(feature.properties["douches"]){ + let shower_icon = document.createElement("img") + shower_icon.src = new URL("../icons/douches.svg", import.meta.url).toString() + shower_icon.alt = "Icone de douche" + shower_icon.title = "Douches" + shower_icon.classList.add("shower") + iconEl.append(shower_icon) + } + + if(feature.properties["n couchage"]){ + let dorm_icon = document.createElement("img") + dorm_icon.src = new URL("../icons/dortoir.svg", import.meta.url).toString() + dorm_icon.alt = "Icone de dortoir" + dorm_icon.title = "Dortoir" + dorm_icon.classList.add("dorm") + iconEl.append(dorm_icon) + } + + let icon = L.divIcon({ + className: "map-amenity-icon", + html: iconEl + }) + + return L.marker(latlng, { + icon: icon + }) + }, + onEachFeature: function(feature, layer){ + if(feature[PARENT_FEATURE]){ + feature[PARENT_FEATURE][AMENITY_MARKER_LAYER] = layer + } else { + feature[AMENITY_MARKER_LAYER] = layer + } + } + }).addTo(map) // La couche des zones disponibles const area_highlight = L.geoJSON(null, { pointToLayer: function(feature, latlng) { - let symbol = feature.mapSymbol; - if(symbol.markerUrl) { - - let iconEl = document.createElement("img"); - iconEl.src = symbol.markerUrl; - - if(iconEl){ - return L.marker(latlng, { - icon: L.divIcon({ - className: "highlight-point-icon", - html: iconEl, - iconSize: [0, 0] - }) - }) - } - } + return L.marker(latlng, { + icon: HIGHLIGHT_MARKER_ICON + }) }, style: function(feature){ - let symbol = feature.mapSymbol; return { className: "map-hilight-area", - fill: !!symbol.backgroundColor, - fillColor: symbol.backgroundColor, - fillOpacity: 0.5, - stroke: !!symbol.borderColor, - color: symbol.borderColor, + fill: false, + stroke: false } }, onEachFeature: function(feature, layer){ - feature[HIGHLIGHT_LAYER] = layer + if(feature[PARENT_FEATURE]){ + feature[PARENT_FEATURE][HIGHLIGHT_LAYER] = layer + } else { + feature[HIGHLIGHT_LAYER] = layer + } } }).addTo(map) @@ -137,9 +187,22 @@ export async function init_places(places_db){ for(let [_feature_id, feature] of Object.entries(places.featuresById)){ area_highlight.addData(feature) - let point_feature = feature.asPoint() - if(point_feature !== feature){ - area_highlight.addData(point_feature) + if(feature.properties["toilettes"] + || feature.properties["douches"] + || feature.properties["eau potable"] + || feature.properties["n couchage"] + ) { + let amenity_feature + + if(feature.geometry.type != "Point"){ + amenity_feature = turf.centerOfMass(feature) + amenity_feature[PARENT_FEATURE] = feature + amenity_feature.properties = feature.properties + } else { + amenity_feature = feature + } + + amenities.addData(amenity_feature) } } } diff --git a/js/places.js b/js/places.js index aebbb17..34e34f0 100644 --- a/js/places.js +++ b/js/places.js @@ -1,11 +1,10 @@ import lunr from "./lib/lunr/lunr.js" import lunrStemmer from "./lib/lunr/lunr.stemmer.support.js" import lunrFr from "./lib/lunr/lunr.fr.js" -import { MapSymbol } from "./symbols.js" lunrStemmer(lunr) lunrFr(lunr) -const FEATURE_INDEX = Symbol("Feature index") +const FEATURE_I = Symbol("Feature index") export const FEATURE_ID = Symbol("Feature id") /** @@ -93,29 +92,22 @@ export class PlaceDatabase extends EventTarget { let all_ids = [] if(geojson.type == "Feature"){ + let featureId = geojson?.properties?.id || geojson?.properties?.[FEATURE_I] + let full_id = idPrefix+"#"+featureId - let feature = geojson; - if(!(feature instanceof MapFeature)){ - feature = new MapFeature(geojson) + if(this.featuresById[full_id]){ + console.warn(`Warning: a feature with ID "${full_id}" already exists in database`) } - if(!feature.id){ - let full_id = idPrefix+"#"+(geojson?.properties?.id || geojson?.properties?.[FEATURE_INDEX]) - feature.id = full_id - } - - if(this.featuresById[feature.id]){ - console.warn(`Warning: a feature with ID "${feature.id}" already exists in database`) - } - - this.featuresById[feature.id] = feature + geojson[FEATURE_ID] = full_id + this.featuresById[full_id] = geojson let event = new Event("newfeature") - event.ref = feature.id - event.feature = feature + event.ref = full_id + event.feature = geojson this.dispatchEvent(event) - all_ids.push(feature.id) + all_ids.push(full_id) } else if(geojson.type == "FeatureCollection") { @@ -123,15 +115,15 @@ export class PlaceDatabase extends EventTarget { idPrefix += (idPrefix != "" ? "-" : "")+geojson.name } - if(geojson?.properties?.[FEATURE_INDEX]){ - idPrefix += (idPrefix != "" ? "-" : "")+geojson.properties[FEATURE_INDEX] + if(geojson?.properties?.[FEATURE_I]){ + idPrefix += (idPrefix != "" ? "-" : "")+geojson.properties[FEATURE_I] } for(let [i, feature] of Object.entries(geojson.features)){ feature.properties = { ...(feature.properties || {}), - [FEATURE_INDEX]: i + [FEATURE_I]: i } let id = this.addFeature(feature, { @@ -167,21 +159,19 @@ export class PlaceDatabase extends EventTarget { this.field("synonyms") for(let [id, feature] of Object.entries(database.featuresById)){ - let sym = feature.mapSymbol; + let synonyms = "" - let synonyms = [] - - if(sym.genericName){ - synonyms.push(sym.genericName) + if(feature.properties["n couchage"] > 0){ + synonyms += SLEEPING_SYNONYMS.join(" ") } - if(sym.indexSynonyms){ - synonyms.push(...sym.indexSynonyms) + if(feature.properties["toilettes"]){ + synonyms += " "+TOILETTES_SYNONYMS.join(" ") } this.add({ id, - name: feature.properties.name || sym.genericName, + name: feature.properties.name, synonyms }) } @@ -194,27 +184,15 @@ export class PlaceDatabase extends EventTarget { let default_database = new PlaceDatabase() await Promise.all([ - default_database.loadGeojson(new URL("../couches/aire-de-jeux.geojson", import.meta.url), {batch: true}), - default_database.loadGeojson(new URL("../couches/autogestion.geojson", import.meta.url), {batch: true}), - default_database.loadGeojson(new URL("../couches/bac-a-sable.geojson", import.meta.url), {batch: true}), default_database.loadGeojson(new URL("../couches/batiments.geojson", import.meta.url), {batch: true}), - default_database.loadGeojson(new URL("../couches/braseros.geojson", import.meta.url), {batch: true}), - default_database.loadGeojson(new URL("../couches/camping.geojson", import.meta.url), {batch: true}), - default_database.loadGeojson(new URL("../couches/dons.geojson", import.meta.url), {batch: true}), - default_database.loadGeojson(new URL("../couches/eau-potable.geojson", import.meta.url), {batch: true}), - default_database.loadGeojson(new URL("../couches/espace-camp.geojson", import.meta.url), {batch: true}), - default_database.loadGeojson(new URL("../couches/espacemiam.geojson", import.meta.url), {batch: true}), - default_database.loadGeojson(new URL("../couches/marabouts.geojson", import.meta.url), {batch: true}), - default_database.loadGeojson(new URL("../couches/medic.geojson", import.meta.url), {batch: true}), - default_database.loadGeojson(new URL("../couches/parkings.geojson", import.meta.url), {batch: true}), - default_database.loadGeojson(new URL("../couches/pieces-batiments.geojson", import.meta.url), {batch: true}), default_database.loadGeojson(new URL("../couches/piscine.geojson", import.meta.url), {batch: true}), - default_database.loadGeojson(new URL("../couches/poubelles.geojson", import.meta.url), {batch: true}), - default_database.loadGeojson(new URL("../couches/scenes.geojson", import.meta.url), {batch: true}), default_database.loadGeojson(new URL("../couches/terrain-de-basket.geojson", import.meta.url), {batch: true}), - default_database.loadGeojson(new URL("../couches/wifi.geojson", import.meta.url), {batch: true}), + default_database.loadGeojson(new URL("../couches/pieces-batiments.geojson", import.meta.url), {batch: true}), + default_database.loadGeojson(new URL("../couches/camping.geojson", import.meta.url), {batch: true}), + default_database.loadGeojson(new URL("../couches/eau-potable.geojson", import.meta.url), {batch: true}), default_database.loadGeojson(new URL("../couches/zones-interdite.geojson", import.meta.url), {batch: true}), - ]) + default_database.loadGeojson(new URL("../couches/parkings.geojson", import.meta.url), {batch: true}), + ]) default_database.buildIndex() @@ -223,51 +201,32 @@ export class PlaceDatabase extends EventTarget { } -const POINT_FEATURE = Symbol("Point feature") +const SLEEPING_SYNONYMS = [ + "dodo", + "dortoir", + "mimir", + "dormir", + "lit" +] -export class MapFeature { - constructor(geojson){ - Object.assign(this, geojson) - } - - get id(){ - return this[FEATURE_ID] - } - - set id(value){ - this[FEATURE_ID] = value - } - - get mapSymbol(){ - return MapSymbol.fromFeature(this) - } - - asPoint(){ - if(!this[POINT_FEATURE]){ - if(!this.geometry){ - debugger; - } - if(this.geometry.type != "Point"){ - let point_feature = turf.centerOfMass(this) - point_feature.properties = this.properties - point_feature = new MapSubFeature(point_feature, this) - this[POINT_FEATURE] = point_feature - } else { - this[POINT_FEATURE] = this - } - } - return this[POINT_FEATURE] - } -} - -const PARENT_FEATURE = Symbol("Parent feature") -export class MapSubFeature extends MapFeature { - constructor(geojson, parent){ - super(geojson) - this[PARENT_FEATURE] = parent - } - - get parentFeature(){ - return this[PARENT_FEATURE] - } -} +const TOILETTES_SYNONYMS = [ + "caca", + "pipi", + "cabinets", + "water-closet", + "latrines", + "🚽", + "double véssé", + "chaise percée", + "WC", + "waters", + "toilettes", + "toilette", + "chiotte", + "chiottes", + "le trône", + "le trones", + "pièce mystère", + "backroom", + "retailleau" +] diff --git a/js/symbols.js b/js/symbols.js deleted file mode 100644 index 310a506..0000000 --- a/js/symbols.js +++ /dev/null @@ -1,204 +0,0 @@ -import { MapFeature } from "./places.js" - -export class MapSymbol { - - /** - * Solid background color of this area (if any) - * @type {undefined|string} - */ - backgroundColor = undefined - - /** - * Background raster tiled image to put on top of solid background (if any) - * @type {undefined|string|URL} - */ - backgroundUrl = undefined - - /** - * Border color of area (if any) - * @type {undefined|string} - */ - borderColor = undefined - - /** - * Marker URL to place on feature (if point) or on centroid (if area) - * @type {undefined|string|URL} - */ - markerUrl = undefined - - /** - * Generic name of this feature if no more precise name is given - * @type {undefined|string} - */ - genericName = undefined - - /** - * A list of sysnonyms for this feature for full text indexing - * @type {string[]} - */ - indexSynonyms = [] - - clone(){ - let new_symb = new MapSymbol() - Object.assign(new_symb, this) - return new_symb - } - - static fromFeature(feature){ - return getSymbolForFeature(feature) - } -} - -/** - * Get a map symbol from feature properties - * @param {MapFeature|Object} feature - */ -function getSymbolForFeature(feature){ - let sym = getBaseSymbolForFeature(feature); - - if(feature.properties["color"]){ - sym = sym.clone() - sym.backgroundColor = feature.properties["color"] - sym.borderColor = feature.properties["color"] - } - - return sym -} - -function getBaseSymbolForFeature(feature){ - - if(feature.properties["wifi"]){ - return WIFI_SYMBOL - } - - if(feature.properties["zone-interdite"]){ - return VERBOTEN_AREA_SYMBOL - } - - if(feature.properties["piscine"]){ - return SWIMMING_POOL_SYMBOL - } - - if(feature.properties["Parking"]){ - return PARKING_SYMBOL - } - - if(feature.properties["n couchage"] > 0){ - return SLEEPING_SYMBOL - } - - if(feature.properties["toilettes"]){ - return TOILETS_SYMBOL - } - - if(feature.properties["douches"]){ - return SHOWER_SYMBOL - } - - if(feature.properties["eau potable"]){ - return DRINKING_WATER_SYMBOL - } - - if(feature.properties["batiment"] || feature.properties["piece-batiment"]){ - return BUILDING_SYMBOL - } - - return DEFAULT_SYMBOL -} - -// PREDEFINED Symbols - -export const DEFAULT_SYMBOL = new MapSymbol() -{ - DEFAULT_SYMBOL.markerUrl = new URL("../icons/default-marker.svg", import.meta.url) - DEFAULT_SYMBOL.backgroundColor = "white" - DEFAULT_SYMBOL.borderColor = "white" -} - -export const SLEEPING_SYMBOL = new MapSymbol() -{ - SLEEPING_SYMBOL.markerUrl = new URL("../icons/dortoir.svg", import.meta.url) - SLEEPING_SYMBOL.genericName = "Dortoir" - SLEEPING_SYMBOL.indexSynonyms = [ - "dodo", - "dortoir", - "mimir", - "dormir", - "lit" - ] -} - -export const DRINKING_WATER_SYMBOL = new MapSymbol() -{ - DRINKING_WATER_SYMBOL.markerUrl = new URL("../icons/eau-potable.svg", import.meta.url) - DRINKING_WATER_SYMBOL.genericName = "Point d'eau potable" -} - -export const PARKING_SYMBOL = new MapSymbol() -{ - PARKING_SYMBOL.markerUrl = new URL("../icons/parking.svg", import.meta.url) - PARKING_SYMBOL.genericName = "Parking" - PARKING_SYMBOL.backgroundColor = "#2d2d2d" - PARKING_SYMBOL.borderColor = "#989898" -} - -export const TOILETS_SYMBOL = new MapSymbol() -{ - TOILETS_SYMBOL.markerUrl = new URL("../icons/toilettes.svg", import.meta.url) - TOILETS_SYMBOL.genericName = "Toilettes" - TOILETS_SYMBOL.indexSynonyms = [ - "caca", - "pipi", - "cabinets", - "water-closet", - "latrines", - "🚽", - "double véssé", - "chaise percée", - "WC", - "waters", - "toilettes", - "toilette", - "chiotte", - "chiottes", - "le trône", - "le trones", - "pièce mystère", - "backroom", - "retailleau" - ] -} - -export const SHOWER_SYMBOL = new MapSymbol() -{ - SHOWER_SYMBOL.markerUrl = new URL("../icons/douches.svg", import.meta.url) - SHOWER_SYMBOL.genericName = "Douche" -} - -export const SWIMMING_POOL_SYMBOL = new MapSymbol() -{ - SWIMMING_POOL_SYMBOL.markerUrl = new URL("../icons/piscine.svg", import.meta.url) - SWIMMING_POOL_SYMBOL.genericName = "Piscine" - SWIMMING_POOL_SYMBOL.backgroundColor = "#366d73" - SWIMMING_POOL_SYMBOL.borderColor = "#366d73" -} - -export const VERBOTEN_AREA_SYMBOL = new MapSymbol() -{ - VERBOTEN_AREA_SYMBOL.genericName = "Zone interdite" - VERBOTEN_AREA_SYMBOL.backgroundColor = "#992700" - VERBOTEN_AREA_SYMBOL.borderColor = "#992700" -} - -export const BUILDING_SYMBOL = new MapSymbol() -{ - BUILDING_SYMBOL.genericName = "Batiment" - BUILDING_SYMBOL.backgroundColor = "#5a5a5a" - BUILDING_SYMBOL.borderColor = "white" -} - -export const WIFI_SYMBOL = new MapSymbol() -{ - WIFI_SYMBOL.genericName = "Wifi" - WIFI_SYMBOL.markerUrl = new URL("../icons/wifi.svg", import.meta.url) -} \ No newline at end of file