{
  "version": 1,
  "examples": [
    {
      "id": "getting-started",
      "group": "1. Getting started",
      "title": "A minimal analytical map",
      "summary": "Power BI latitude/longitude with a retained canvas marker layer.",
      "spec": "getting-started/spec.json",
      "data": "data/analytical-points.json",
      "expected": "The map fits all twelve assets and clicking a point updates the selected count.",
      "limitations": "The browser gallery uses synthetic row identities."
    },
    {
      "id": "data-sources",
      "group": "2. Data sources",
      "title": "Inline GeoJSON",
      "summary": "A declarative FeatureCollection with no executable content.",
      "spec": "sources/spec.json",
      "data": "data/analytical-points.json",
      "expected": "Two inline GeoJSON facilities render independently of the Power BI row set.",
      "limitations": "Remote GeoJSON additionally requires provider host permission."
    },
    {
      "id": "point-symbols",
      "group": "3. Point symbols",
      "title": "Simple point symbols",
      "summary": "Safe circle symbols with explicit outline and opacity.",
      "spec": "symbols/points/spec.json",
      "data": "data/analytical-points.json",
      "expected": "Every asset is drawn as a teal circle with a dark outline.",
      "limitations": "Large point sets should use clustering or heatmap rendering."
    },
    {
      "id": "line-polygon",
      "group": "4. Line and polygon symbols",
      "title": "Line and polygon styling",
      "summary": "Inline operational route and service-area geometry.",
      "spec": "symbols/line-polygon/spec.json",
      "data": "data/analytical-points.json",
      "expected": "A dashed route overlays a translucent service polygon.",
      "limitations": "Fill patterns are intentionally partial; solid fills are fully supported."
    },
    {
      "id": "unique-breaks",
      "group": "5. Unique values and class breaks",
      "title": "Authored unique values",
      "summary": "Categorical status styling with deterministic fallback.",
      "spec": "renderers/unique-values/spec.json",
      "data": "data/analytical-points.json",
      "expected": "Active, warning, and offline assets use distinct authored symbols.",
      "limitations": "Unmapped values use the authored default symbol."
    },
    {
      "id": "continuous-size",
      "group": "6. Continuous color and proportional size",
      "title": "Proportional asset value",
      "summary": "Data-driven size with a quantitative legend.",
      "spec": "renderers/proportional-size/spec.json",
      "data": "data/analytical-points.json",
      "expected": "Higher-value assets draw larger and the legend shows the size domain.",
      "limitations": "Domains are cached from the current resolved layer."
    },
    {
      "id": "rich-icons",
      "group": "7. Rich icons",
      "title": "Mapped and rotated icons",
      "summary": "Built-in icons, categorical mapping, rotation, labels, and badges.",
      "spec": "symbols/rich-icons/spec.json",
      "data": "data/analytical-points.json",
      "expected": "Each category receives a safe built-in icon rotated by heading.",
      "limitations": "SVG is sanitized and image icons must be HTTPS or relative."
    },
    {
      "id": "labels",
      "group": "8. Labels",
      "title": "Responsive feature labels",
      "summary": "Name labels with zoom and collision controls.",
      "spec": "labels/spec.json",
      "data": "data/analytical-points.json",
      "expected": "Asset names appear at the authored zoom range.",
      "limitations": "Label collision is screen-space based, not cartographic annotation."
    },
    {
      "id": "popups",
      "group": "9. Popups and tooltips",
      "title": "Safe structured details",
      "summary": "Declarative tooltip and popup fields without HTML injection.",
      "spec": "popups/spec.json",
      "data": "data/analytical-points.json",
      "expected": "Hover reveals a tooltip; click opens synchronized asset details.",
      "limitations": "Unrestricted HTML and scripts are never accepted."
    },
    {
      "id": "legends",
      "group": "10. Interactive legends",
      "title": "Filterable categorical legend",
      "summary": "Counts, percentages, aggregate values, ordering, search, and isolate.",
      "spec": "legends/spec.json",
      "data": "data/analytical-points.json",
      "expected": "Legend clicks filter status categories; hover emphasizes matching features.",
      "limitations": "Legend search appears when the category list is large."
    },
    {
      "id": "internal-filtering",
      "group": "11. Internal filtering",
      "title": "Legend-driven map filtering",
      "summary": "A layer-scoped internal filter that retains interaction state.",
      "spec": "interactions/internal/spec.json",
      "data": "data/analytical-points.json",
      "expected": "Selecting a legend category filters only the assets layer.",
      "limitations": "It does not replace the Power BI filter pane."
    },
    {
      "id": "external-interaction",
      "group": "12. External interaction",
      "title": "Power BI selection pipeline",
      "summary": "Internal highlight plus external host selection identities.",
      "spec": "interactions/external/spec.json",
      "data": "data/analytical-points.json",
      "expected": "Feature and legend selection share the same row identity pipeline.",
      "limitations": "The standalone gallery cannot call a Power BI host selection manager.",
      "powerBi": "In the visual host, each resolved feature carries its retained Power BI selection identities."
    },
    {
      "id": "feature-selection",
      "group": "13. Feature selection",
      "title": "Click, visible, invert, and clear",
      "summary": "Compact selection control and row/feature counts.",
      "spec": "selection/click/spec.json",
      "data": "data/analytical-points.json",
      "expected": "Clicking selects a point; the selection menu can select visible, invert, zoom, or clear.",
      "limitations": "Power BI identity submission defaults to a 1,000-identity safety limit."
    },
    {
      "id": "spatial-selection",
      "group": "14. Rectangle, lasso, and circle selection",
      "title": "Spatial selection modes",
      "summary": "Rectangle, lasso, and geodesic radius tools with modifiers.",
      "spec": "selection/spatial/spec.json",
      "data": "data/analytical-points.json",
      "expected": "Drag any enabled shape; Shift adds, Ctrl/Cmd toggles, and Alt subtracts.",
      "limitations": "Selection targets resolved feature geometry, not heatmap pixels."
    },
    {
      "id": "clustering",
      "group": "15. Clustering",
      "title": "Selectable point clusters",
      "summary": "Dense points grouped without unnecessary DOM markers.",
      "spec": "clustering/spec.json",
      "data": "data/analytical-points.json",
      "expected": "Nearby assets group at low zoom and separate as the map zooms in.",
      "limitations": "Cluster aggregation supports count or one numeric sum."
    },
    {
      "id": "heatmap",
      "group": "16. Real heatmaps",
      "title": "Weighted canvas heatmap",
      "summary": "True intensity pixels with viewport normalization and retained hit points.",
      "spec": "heatmap/spec.json",
      "data": "data/analytical-points.json",
      "expected": "A weighted raster updates when filters or external highlights change.",
      "limitations": "Pixels are not selectable; optional transparent source points provide hit testing."
    },
    {
      "id": "density-grid",
      "group": "17. Density grid",
      "title": "Viewport density grid",
      "summary": "Count aggregation into stable screen-space cells.",
      "spec": "renderers/density-grid/spec.json",
      "data": "data/analytical-points.json",
      "expected": "Assets aggregate into colored density cells.",
      "limitations": "Density cells summarize points and are not editable GIS features."
    },
    {
      "id": "arcgis-feature",
      "group": "18. ArcGIS Feature Service",
      "title": "ArcGIS feature query",
      "summary": "Viewport-aware service features with service renderer support.",
      "spec": "arcgis/feature/spec.json",
      "data": "data/analytical-points.json",
      "expected": "The public service is queried for the current viewport.",
      "limitations": "Requires network access and an allowed ArcGIS provider host."
    },
    {
      "id": "arcgis-tile",
      "group": "19. ArcGIS Tile Service",
      "title": "ArcGIS cached tiles",
      "summary": "Read-only operational tile overlay.",
      "spec": "arcgis/tile/spec.json",
      "data": "data/analytical-points.json",
      "expected": "Cached ArcGIS tiles render beneath analytical points.",
      "limitations": "Raster tile pixels do not participate in selection."
    },
    {
      "id": "arcgis-dynamic",
      "group": "20. ArcGIS Dynamic Service and identify",
      "title": "Dynamic service identify",
      "summary": "Export-image layer with read-only click identify.",
      "spec": "arcgis/dynamic/spec.json",
      "data": "data/analytical-points.json",
      "expected": "The service image follows the viewport; click identify returns temporary results.",
      "limitations": "Identify results never participate in joins or selection."
    },
    {
      "id": "joins",
      "group": "21. Joins",
      "title": "Service-to-model joins",
      "summary": "Declarative normalized key matching and aggregate policy.",
      "spec": "joins/spec.json",
      "data": "data/analytical-points.json",
      "expected": "Service features resolve model fields through a safe join definition.",
      "limitations": "This live service example depends on stable public service keys."
    },
    {
      "id": "layer-groups",
      "group": "22. Layer groups",
      "title": "Layer groups",
      "summary": "Authored group visibility, opacity, and order.",
      "spec": "tools/layer-groups/spec.json",
      "data": "data/analytical-points.json",
      "expected": "The layer panel exposes the Operations group as one authored unit.",
      "limitations": "Viewer changes are session state and do not mutate the specification."
    },
    {
      "id": "visibility-opacity",
      "group": "23. Layer visibility and opacity",
      "title": "Viewer layer controls",
      "summary": "Layer visibility, opacity, labels, and ordering controls.",
      "spec": "tools/layers/spec.json",
      "data": "data/analytical-points.json",
      "expected": "The layer panel toggles assets and changes retained layer opacity.",
      "limitations": "Only controls explicitly enabled by the author appear."
    },
    {
      "id": "bookmarks",
      "group": "24. Bookmarks and navigation",
      "title": "Authored map bookmarks",
      "summary": "Home extent, bookmarks, and zoom-to-selection.",
      "spec": "tools/bookmarks/spec.json",
      "data": "data/analytical-points.json",
      "expected": "Bookmark buttons move between downtown and the full operating area.",
      "limitations": "Bookmarks store view only, not complete filter state."
    },
    {
      "id": "search",
      "group": "25. Search",
      "title": "Provider-backed location search",
      "summary": "User-triggered geocoding with safe result navigation.",
      "spec": "tools/search/spec.json",
      "data": "data/analytical-points.json",
      "expected": "The search panel is enabled when a geocoder provider is configured.",
      "limitations": "No network request occurs without an enabled provider configuration."
    },
    {
      "id": "quick-filters",
      "group": "26. Quick filters",
      "title": "In-map analytical filters",
      "summary": "Category, range, date, text, null, top N, and filter-to-selected.",
      "spec": "tools/quick-filters/spec.json",
      "data": "data/analytical-points.json",
      "expected": "The filter panel combines lightweight filters and can clear them atomically.",
      "limitations": "Quick filters are local analytical state, not a full filter-pane replacement."
    },
    {
      "id": "performance",
      "group": "27. Performance controls",
      "title": "Performance-aware rendering",
      "summary": "Viewport culling, cached domains, canvas density, and warnings.",
      "spec": "performance/spec.json",
      "data": "data/analytical-points.json",
      "expected": "The layer uses authored culling and feature-limit controls.",
      "limitations": "Threshold warnings are guidance; authors still choose appropriate service queries."
    },
    {
      "id": "responsive",
      "group": "28. Responsive behavior",
      "title": "Compact responsive tools",
      "summary": "A single compact toolbar, responsive panels, scale, and coordinates.",
      "spec": "tools/responsive/spec.json",
      "data": "data/analytical-points.json",
      "expected": "Controls remain usable at narrow widths without covering the map.",
      "limitations": "Extremely small containers prioritize map interaction over secondary labels."
    },
    {
      "id": "advanced",
      "group": "29. Combined advanced analytical example",
      "title": "Operations command map",
      "summary": "Rich icons, legends, quick filters, spatial selection, heat, and linked interaction.",
      "spec": "advanced/spec.json",
      "data": "data/analytical-points.json",
      "expected": "Two layers remain synchronized through filtering, selection, hover, and counts.",
      "limitations": "The gallery uses small deterministic data; production service limits still apply."
    },
    {
      "id": "remote-analytics",
      "group": "30. Dynamic Content",
      "title": "Dynamic GIS Analytics",
      "summary": "Click a water-pressure sensor to fetch KPIs, a zoomable chart and measurements through the shared runtime.",
      "spec": "../dashboards/dynamic-gis-analytics/specification.json",
      "data": "../dashboards/dynamic-gis-analytics/data.csv",
      "dashboard": "dynamic-gis-analytics",
      "expected": "Three clickable monitoring stations load validated remote HyperPBI fragments. Expand, zoom, refresh and switch sensors.",
      "limitations": "Deterministic synthetic data. The public HTTPS API requires CORS and approved WebAccess when used from Power BI.",
      "powerBi": "Maps package required. Fragment data stays local and never cross-filters the Power BI model."
    }
  ]
}
