Skip to content

feat(yeti): YETI updates for 2025/2026#4457

Merged
brunobesson merged 24 commits intoc2corg:masterfrom
iamvdo:yeti-2025
Nov 19, 2025
Merged

feat(yeti): YETI updates for 2025/2026#4457
brunobesson merged 24 commits intoc2corg:masterfrom
iamvdo:yeti-2025

Conversation

@iamvdo
Copy link
Copy Markdown
Contributor

@iamvdo iamvdo commented Nov 6, 2025

Hello everyone,

Here is a PR for YETI app. Let's resume key features:

  • New "Protected Areas" layer, for France and Switzerland (swiss tranquility zones, biodiv)
  • New "Meteo" tab, to get meteo data from meteoblue.com
  • "Winter routes" layer now with entire Switzerland data
  • "My Outing" tab reorganized
    • Better buttons
    • Winter route layer included (ability to select/convert a route to an outing)
  • New editing features
    • Show distance and bearing when drawing new route
    • Split interaction for routes (eg, split a single route in two)
  • Refactor and bug fixes

Thanks for review and merging.

Screenshots:
image
image
image
image
image

Summary by CodeRabbit

  • New Features

    • Meteo panel & tab with map-click weather widget; Meteo map layer and Protected Areas layer added.
  • Improvements

    • Route editor: ctrl‑click split, live distance/bearing overlay and contextual key hints.
    • Dropdown now accepts an initial open prop.
    • Edit button supports small/icon‑only modes and accessibility labels.
    • SubPanelCourse: import UI, conditional export/import sections, winter-route support.
    • Avalanche timing window shortened (weeks validity reduced).

@iamvdo iamvdo changed the title YETI updates for 2025/2026 feat(yeti): YETI updates for 2025/2026 Nov 6, 2025
@github-actions github-actions bot added the enhancement New feature or request label Nov 6, 2025
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Nov 6, 2025

Walkthrough

Adds Meteo and Protected Areas map layers and a Meteo panel, centralizes tab state and protectedAreas persistence in Yetix, enhances route editing with split interaction and live draw metrics, adjusts multiple layer behaviors/UI props, and introduces several new components and OL/font-awesome exports.

Changes

Cohort / File(s) Change Summary
Dropdown & Edit controls
src/components/yeti/DropdownContent.vue, src/components/yeti/EditModeButton.vue
DropdownContent.vue: new open prop and openedlocalOpen rename; template/methods use localOpen. EditModeButton.vue: adds isSmall and iconOnly props, accessibility attrs, and conditional label.
SubPanelCourse & YetiMap UI tweaks
src/components/yeti/SubPanelCourse.vue, src/components/yeti/YetiMap.vue
SubPanelCourse.vue: reorganized import/export UI, winter-route layer support, conditional sections (Import/Export/No-route/help tips). YetiMap.vue: added inline height: 0 on edit-mode container.
New Meteo panel & layer
src/components/yeti/SubPanelMeteo.vue, src/components/yeti/map-layers/MeteoLayer.vue
New SubPanelMeteo.vue: subscribes to lon-lat, builds Meteoblue widget URLs, shows iframe with loading state. MeteoLayer.vue: FakeTile + marker layers, click→lon/lat, emits Yetix.$emit('lon-lat', {lon,lat}), visible prop, marker styling.
Protected Areas integration
src/components/yeti/map-layers/ProtectedAreasLayer.vue
New ProtectedAreasLayer.vue: layer group + overlay UI, concurrent data queries (Promise.any) to multiple services, multilingual content parsing, overlay positioning/state, visibility toggle and error handling.
OverlaysLayers & tab wiring
src/components/yeti/map-layers/OverlaysLayers.vue, src/views/portals/YetiView.vue
OverlaysLayers.vue: imports/registers MeteoLayer and ProtectedAreasLayer, reorders layer indices, adds activeTab(), tabs(), meteoTab() computed props, improves raster pixel extraction (handles Tile Groups). YetiView.vue: registers SubPanelMeteo, centralizes tab construction via setTabs() and pushes to Yetix.setTabs().
Yetix state & persistence
src/components/yeti/Yetix.vue
Adds tabs and showProtectedAreas to defaultState; adds mutations setTabs and setShowProtectedAreas (persists protectedAreas to localStorage); changes default showAvalancheBulletins false; exposes new getters.
Route editing enhancements
src/components/yeti/map-layers/RouteLayer.vue
Adds non-exported SplitInteraction (ctrl-click split), drawInfo + drawInfoOverlay with Draw geometryFunction computing live distance/bearing, onSplit, onKeydown/onKeyup and window blur cleanup, UI hints via FontAwesome icons.
Avalanche / Data / Nivoses / FFVL tweaks
src/components/yeti/map-layers/AvalancheBulletinsLayer.vue, src/components/yeti/map-layers/DataAvalancheLayer.vue, src/components/yeti/map-layers/NivosesLayer.vue, src/components/yeti/map-layers/FfvlLayer.vue
AvalancheBulletinsLayer.vue: conditional mountains fetch, mount/listener sequencing, Turf buffer uses { steps: 1 }, CSS user-select change. DataAvalancheLayer.vue: daysValidWeeks 15→5. NivosesLayer.vue: adjusted numbers (3 months, 7 days) and translation wrapping. FfvlLayer.vue: added translate-context="yeti".
Winter route adjustments
src/components/yeti/map-layers/WinterRouteLayer.vue, src/components/yeti/map-layers/WinterRouteLayerContent.vue
WinterRouteLayer.vue: layerSelector id 'winter-route', removed Tile extent. WinterRouteLayerContent.vue: route title falls back to localized name_{lang} or name when start/stop missing.
OL & icons exports
src/js/libs/ol.js, src/js/vue-plugins/font-awesome-config.js
js/libs/ol.js: exports Pointer, ImageTile, and sphere.getDistance. font-awesome-config.js: registers faPlusMinus and faScissors.
New files (summary)
src/components/yeti/SubPanelMeteo.vue, src/components/yeti/map-layers/MeteoLayer.vue, src/components/yeti/map-layers/ProtectedAreasLayer.vue
Added three Vue SFCs: Meteo panel, Meteo map layer, Protected Areas map layer (see cohorts above).

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Map
    participant MeteoLayer
    participant Yetix
    participant SubPanelMeteo

    User->>Map: click
    Map->>MeteoLayer: onMapClick(coordinate)
    MeteoLayer->>MeteoLayer: convert coordinate → lon/lat, place marker
    MeteoLayer->>Yetix: emit 'lon-lat' {lon,lat}
    Yetix->>SubPanelMeteo: deliver event
    SubPanelMeteo->>SubPanelMeteo: build widget URL (lang, lat, lon)
    SubPanelMeteo->>User: render iframe
Loading
sequenceDiagram
    participant User
    participant RouteLayer
    participant DrawOverlay
    participant Map

    User->>RouteLayer: start drawing
    loop each point
        User->>RouteLayer: add point
        RouteLayer->>RouteLayer: compute distance & bearing
        RouteLayer->>DrawOverlay: update metrics & position
        DrawOverlay->>User: show live metrics
    end
    User->>RouteLayer: Ctrl+Click segment
    RouteLayer->>RouteLayer: SplitInteraction splits → emit 'split'
    RouteLayer->>Map: onSplit applies new features, removes old
Loading
sequenceDiagram
    participant User
    participant ProtectedAreasLayer
    participant SwissService
    participant BioDiv
    participant Overlay

    User->>ProtectedAreasLayer: click map
    par concurrent requests
        ProtectedAreasLayer->>SwissService: fetch protected-area info
    and
        ProtectedAreasLayer->>BioDiv: fetch WMS GetFeatureInfo
    end
    ProtectedAreasLayer->>ProtectedAreasLayer: Promise.any → choose first result
    ProtectedAreasLayer->>Overlay: set overlayData and show
    User->>Overlay: interact / close
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60–90 minutes

  • New complex components: ProtectedAreasLayer (concurrent fetch, multilingual parsing, overlay positioning) and MeteoLayer/SubPanelMeteo (map click → iframe integration).
  • RouteLayer split interaction and live geometry logic require careful review of interaction wiring, geometryFunction math, and cleanup.
  • Cross-file state changes: Yetix persistence (protectedAreas, tabs) affect multiple consumers.
  • OverlaysLayers: raster pixel extraction across Tile Groups and layer reordering need verification.

Files/areas to inspect closely:

  • src/components/yeti/map-layers/ProtectedAreasLayer.vue — Promise.any error paths, overlayData shaping, panning logic.
  • src/components/yeti/map-layers/RouteLayer.vue — SplitInteraction correctness, feature replacement and event un/registration.
  • src/components/yeti/map-layers/OverlaysLayers.vue — Group layer pixel iteration and layer index consistency.
  • src/components/yeti/Yetix.vue — localStorage read/write for overlays including protectedAreas and tabs propagation.

Poem

🐰 I hopped across the map at night,

New layers bloomed beneath my sight,
A marker, widget, split with glee,
Protected fields woke up with me,
Happy rabbit — code takes flight!

Pre-merge checks and finishing touches

❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Title check ❓ Inconclusive The title 'feat(yeti): YETI updates for 2025/2026' is partially related to the changeset but overly broad and vague. It fails to highlight the main changes (new Protected Areas layer, Meteo tab, split interaction, etc.) and reads more like a generic release note. Consider using a more specific title that captures the primary feature being added, such as 'feat(yeti): add protected areas layer and meteo tab' or 'feat(yeti): expand winter routes and add route editing features'.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (1)
src/components/yeti/DropdownContent.vue (1)

26-39: Keep the dropdown synced with the new prop.

The new open prop seeds localOpen, but once the component is mounted a parent update to open won’t be reflected because nothing watches the prop. Please mirror the prop so the dropdown can be controlled externally.

 export default {
   props: {
@@
   data() {
     return {
       localOpen: this.open,
     };
   },
+  watch: {
+    open(newValue) {
+      this.localOpen = newValue;
+    },
+  },
   methods: {
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f929053 and a58e5f9.

⛔ Files ignored due to path filters (1)
  • src/assets/img/yeti/layers/protected-areas.jpg is excluded by !**/*.jpg
📒 Files selected for processing (19)
  • src/components/yeti/DropdownContent.vue (2 hunks)
  • src/components/yeti/EditModeButton.vue (2 hunks)
  • src/components/yeti/SubPanelCourse.vue (4 hunks)
  • src/components/yeti/SubPanelMeteo.vue (1 hunks)
  • src/components/yeti/YetiMap.vue (1 hunks)
  • src/components/yeti/Yetix.vue (8 hunks)
  • src/components/yeti/map-layers/AvalancheBulletinsLayer.vue (4 hunks)
  • src/components/yeti/map-layers/DataAvalancheLayer.vue (1 hunks)
  • src/components/yeti/map-layers/FfvlLayer.vue (1 hunks)
  • src/components/yeti/map-layers/MeteoLayer.vue (1 hunks)
  • src/components/yeti/map-layers/NivosesLayer.vue (1 hunks)
  • src/components/yeti/map-layers/OverlaysLayers.vue (5 hunks)
  • src/components/yeti/map-layers/ProtectedAreasLayer.vue (1 hunks)
  • src/components/yeti/map-layers/RouteLayer.vue (7 hunks)
  • src/components/yeti/map-layers/WinterRouteLayer.vue (1 hunks)
  • src/components/yeti/map-layers/WinterRouteLayerContent.vue (1 hunks)
  • src/js/libs/ol.js (4 hunks)
  • src/js/vue-plugins/font-awesome-config.js (2 hunks)
  • src/views/portals/YetiView.vue (6 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: build (lts/*)
  • GitHub Check: CodeQL-Build
🔇 Additional comments (2)
src/components/yeti/map-layers/DataAvalancheLayer.vue (1)

54-54: Change is intentional and correctly implemented.

The change from 15 to 5 days is documented in commit b6daa98 (feat(yeti): Switch icon color from 15 to 5 days, Feb 17 2025). The code is syntactically correct and properly integrated into the color-selection logic for Data-Avalanche features.

The commit message confirms the feature intent. However, verify that the 5-day threshold aligns with your avalanche data practices and user expectations for the season.

src/components/yeti/map-layers/FfvlLayer.vue (1)

17-17: LGTM! Good i18n practice.

Adding translate-context="yeti" helps translators disambiguate "History" within the YETI module context.

Verification shows that translate-context is selectively applied across YETI components (notably for action verbs in SubPanelCourse.vue), making this change consistent with the codebase's i18n approach.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (5)
src/components/yeti/map-layers/RouteLayer.vue (5)

57-65: Consider simplifying the insertion guard logic.

Lines 59-60 check whether coordinates[insertIndex] differs from the split point, but since insertIndex points to the coordinate after the segment where the split point was found, this check is always true and can be removed. Additionally, the check on lines 61-62 prevents splitting when the split point coincides with the first vertex, which may be an unnecessary restriction.

Consider simplifying to:

           if (
             insertIndex !== undefined &&
-            coordinates[insertIndex][0] !== this.coordinate_[0] &&
-            coordinates[insertIndex][1] !== this.coordinate_[1] &&
-            coordinates[0][0] !== this.coordinate_[0] &&
-            coordinates[0][1] !== this.coordinate_[1]
+            !coordinates.some(c => c[0] === this.coordinate_[0] && c[1] === this.coordinate_[1])
           ) {

This checks that the split point is not already a vertex, which is the actual intent.


101-109: Consider adjusting pointOnSegment tolerance for robustness.

The cross-product tolerance of 1e-3 may be too strict for typical map coordinate systems, potentially causing false negatives due to floating-point precision. Consider increasing it to 1e-1 or making it configurable.


413-419: Simplify distance check.

The if (distance > 0) check is unnecessary since Math.round() handles zero correctly.

-          if (distance > 0) {
-            this.drawInfo.distance = Math.round(distance);
-            this.drawInfo.bearing = Math.round(bearing);
-          } else {
-            this.drawInfo.distance = 0;
-            this.drawInfo.bearing = 0;
-          }
+          this.drawInfo.distance = Math.round(distance);
+          this.drawInfo.bearing = Math.round(bearing);

741-754: Module-level editStyle mutation is safe but brittle.

The code mutates the module-level editStyle array, which would be problematic if multiple component instances existed. While this appears designed as a singleton (via Yetix global store), consider moving editStyle into component instance data for better encapsulation.


796-804: Consider enhancing overlay visibility.

The yellow background works, but adding a subtle shadow or border could improve legibility over varied map backgrounds.

 .draw-info-overlay {
   background: yellow;
   padding: 2px;
   border-radius: 2px;
   font-size: 0.85rem;
   min-width: 60px;
+  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
+  border: 1px solid rgba(0, 0, 0, 0.2);
 }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a58e5f9 and 323f0cb.

📒 Files selected for processing (4)
  • src/components/yeti/EditModeButton.vue (2 hunks)
  • src/components/yeti/map-layers/OverlaysLayers.vue (5 hunks)
  • src/components/yeti/map-layers/ProtectedAreasLayer.vue (1 hunks)
  • src/components/yeti/map-layers/RouteLayer.vue (7 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build (lts/*)
🔇 Additional comments (17)
src/components/yeti/EditModeButton.vue (2)

9-9: LGTM!

The conditional rendering correctly hides the text label when iconOnly is true.


19-28: LGTM!

The new props are properly typed with sensible defaults and are correctly consumed in the template.

src/components/yeti/map-layers/RouteLayer.vue (14)

1-6: LGTM! Clean overlay template.

The draw info overlay template is well-structured with appropriate icons and locale-aware number formatting for distance display.


9-9: LGTM!

The FontAwesome icon import is appropriate for the dynamic icon generation used in the keyboard event handlers.


68-83: LGTM! Split logic correctly prevents out-of-bounds access.

The splitDone flag properly addresses the previous review concern about repeated split points in rings or self-crossing lines.


237-241: LGTM!

The overlay configuration is appropriate with sensible positioning and offset values.


276-287: LGTM!

The keyboard event listener lifecycle is properly managed, adding listeners when entering edit mode and cleaning them up when exiting.


303-305: LGTM!

The overlay setup correctly binds the Vue template ref to the OpenLayers overlay and adds it to the map.


385-424: LGTM! Distance and bearing calculations are correct.

The spherical distance calculation and bearing formula are implemented correctly using standard geodetic formulas.


428-431: LGTM!

The split interaction is properly instantiated and configured with the correct layer reference.


439-439: LGTM!

The split event is properly wired to the onSplit handler.


443-457: LGTM!

The split interaction is correctly managed alongside other interactions, and the overlay is properly cleaned up when interactions are disabled.


458-470: The state inconsistency issue was already flagged in the review of lines 248-252.


474-477: LGTM!

The split handler correctly processes the split event by adding new features and removing the original.


719-773: LGTM! Contextual hints provide good UX.

The dynamic icon and tooltip injection based on keyboard modifiers is well-implemented and enhances the editing experience.


774-788: LGTM! Cleanup logic correctly handles keyboard state.

The hint removal correctly checks modifier states and style array length to ensure proper cleanup only when appropriate.

src/components/yeti/map-layers/OverlaysLayers.vue (1)

56-58: Optional chaining guard looks good

The optional chaining keeps meteoTab from exploding when the tabs array is empty, so the overlays stack won’t crash on first render anymore.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b53bf0b and 072c83f.

⛔ Files ignored due to path filters (2)
  • src/assets/img/yeti/logo-ensg.jpg is excluded by !**/*.jpg
  • src/assets/img/yeti/logo-geodataparis.jpg is excluded by !**/*.jpg
📒 Files selected for processing (1)
  • src/views/portals/YetiView.vue (6 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: build (lts/*)
  • GitHub Check: CodeQL-Build
🔇 Additional comments (6)
src/views/portals/YetiView.vue (6)

115-115: LGTM!

The SubPanelMeteo component import and registration follows the established pattern for other sub-panel components in this view.

Also applies to: 135-135


67-69: LGTM!

The new Meteo panel is correctly added with index 3, following the sequential pattern of existing panels and matching the new tab structure.


75-79: Branding update looks good.

The logo link and assets have been consistently updated to Géodata Paris branding. Note that the URL uses http rather than https.


194-194: LGTM!

Centralizing tab state in Yetix improves separation of concerns and aligns with the PR's objective to consolidate tab management.


267-269: LGTM!

Tab initialization in the created hook follows a logical sequence and properly builds and registers tabs with the centralized Yetix state.


643-645: LGTM!

The style rule appropriately adds border-radius to small buttons and is correctly placed in the non-scoped styles section.

@iamvdo
Copy link
Copy Markdown
Contributor Author

iamvdo commented Nov 17, 2025

@brunobesson Hello, any info on this? Thanks

@brunobesson
Copy link
Copy Markdown
Member

I'll have a look asap, sorry

@brunobesson
Copy link
Copy Markdown
Member

brunobesson commented Nov 19, 2025

As usual, very nice work! If I had more time, there is some stuff I would love to have within the rest of c2c too 😍

Functionnaly, the only strage thing I saw is the winter routes for Switzerland. I did not see them? (nothing shown, no request...) The layer switch still has the french flag too.

@brunobesson brunobesson merged commit e5234eb into c2corg:master Nov 19, 2025
9 checks passed
@brunobesson
Copy link
Copy Markdown
Member

(I merged and will release today or within the week despite my comment)

@jbeilin
Copy link
Copy Markdown
Contributor

jbeilin commented Nov 19, 2025

I still have to create a virtual layer in our geoserver with FRA and CHE data.

@jbeilin
Copy link
Copy Markdown
Contributor

jbeilin commented Nov 19, 2025

I have just created an aggregated layer for FRA/CHE data. It will be cropped on the french Alps bbox until #4457 will be released.

@brunobesson
Copy link
Copy Markdown
Member

It has been released today 😁

@iamvdo
Copy link
Copy Markdown
Contributor Author

iamvdo commented Nov 25, 2025

The layer switch still has the french flag too.

Good catch! Will make another PR...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants