Skip to content

Commit 8476b70

Browse files
author
Peter Rushforth
committed
Add skills, copilot-instructions.md
1 parent 813cd02 commit 8476b70

13 files changed

Lines changed: 580 additions & 9 deletions

File tree

.github/copilot-instructions.md

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
# MapMLify AI Coding Instructions
2+
3+
## Project Overview
4+
MapMLify is a client-side web application that converts OGC WMS (Web Map Service) capabilities documents into functional MapML map viewers. It uses the `@maps4html/mapml` web components to create interactive map experiences directly in the browser.
5+
6+
**⚠️ Before coding: Read your available skills/tools to understand what capabilities you have for file editing, searching, running commands, etc.**
7+
8+
## Architecture & Key Concepts
9+
10+
### Core Data Flow
11+
1. **Fetch WMS Capabilities** ([main.js](../src/script/main.js#L83-L106)): Attempts direct fetch first, falls back to CORS proxy (`https://corsproxy.io/?`)
12+
2. **Parse XML Capabilities** ([main.js](../src/script/main.js#L112-L269)): Extracts service info, layers, styles, CRS/SRS, bounding boxes, GetFeatureInfo formats
13+
3. **Generate MapML** ([main.js](../src/script/main.js#L620-L770)): Dynamically creates `<mapml-viewer>`, `<map-layer>`, `<map-extent>`, `<map-input>`, `<map-link>` elements
14+
4. **Render Interactive Maps**: MapML web components handle rendering, interaction, and GetFeatureInfo queries
15+
16+
### WMS Version Handling
17+
The app supports WMS 1.1.1 and 1.3.0, with critical differences:
18+
- **Parameter names**: 1.3.0 uses `CRS`/`I`/`J`, 1.1.1 uses `SRS`/`X`/`Y`
19+
- **BBOX ordering**: WMS 1.3.0 with EPSG:4326 requires lat,lon order (ymin,xmin,ymax,xmax); all other CRS use standard xmin,ymin,xmax,ymax ([main.js](../src/script/main.js#L540-L548))
20+
- Check version with `version.startsWith('1.3')` pattern throughout codebase
21+
22+
### Projection System
23+
Four projections are supported, mapped from WMS CRS to MapML units:
24+
- `EPSG:3857``OSMTILE` (Web Mercator) - default
25+
- `EPSG:3978``CBMTILE` (Canada Lambert Conformal Conic)
26+
- `EPSG:4326` / `CRS:84``WGS84` (Geographic)
27+
- `EPSG:5936``APSTILE` (Arctic Polar Stereographic)
28+
29+
Coordinate transformation uses `wgs84ToWebMercator()` for OSMTILE ([main.js](../src/script/main.js#L6-L11)).
30+
31+
### MapML Structure Pattern
32+
Every layer creates this DOM structure:
33+
```html
34+
<mapml-viewer projection="OSMTILE" controls>
35+
<map-layer label="Layer Title" checked>
36+
<map-link rel="license" href="..." />
37+
<map-link rel="legend" href="..." />
38+
<map-extent units="OSMTILE">
39+
<map-input name="xmin" type="location" units="pcrs" axis="easting" position="top-left" />
40+
<!-- ... more inputs for ymin, xmax, ymax, w, h, i, j ... -->
41+
<map-select name="style"><!-- if styles exist --></map-select>
42+
<map-link rel="image" tref="...{xmin},{ymin},{xmax},{ymax}..." />
43+
<map-link rel="query" tref="..." data-query-link="true" /><!-- if queryable -->
44+
</map-extent>
45+
</map-layer>
46+
</mapml-viewer>
47+
```
48+
49+
## Development Workflows
50+
51+
### Running the App
52+
```bash
53+
npm run serve # Starts http-server on port 8000
54+
# Navigate to http://localhost:8000/src/index.html
55+
```
56+
57+
### Code Formatting
58+
```bash
59+
npm run format # Format all JS files
60+
npm run format:check # Check formatting without changes
61+
```
62+
63+
### Testing WMS Services
64+
- Preset URLs are loaded from [capabilities.txt](../src/capabilities.txt) with format: `Label,URL` or just `URL`
65+
- Test both direct fetch and CORS proxy scenarios
66+
- Verify behavior across WMS versions (1.1.1 vs 1.3.0)
67+
68+
## Project-Specific Conventions
69+
70+
### State Management
71+
- `currentWmsBaseUrl`: Stores base URL (without query params) for building GetMap/GetFeatureInfo requests
72+
- `currentUsedProxy`: Boolean flag indicating if CORS proxy was needed
73+
- Layer index (`data-layer-index`) links checkboxes to viewer containers
74+
75+
### Dynamic UI Updates
76+
When users change dropdowns (style, format, projection), the app:
77+
1. Updates preview thumbnails immediately ([main.js](../src/script/main.js#L408-L414))
78+
2. If viewer is active, calls `removeViewerForLayer()` then `createViewerForLayer()` to rebuild ([main.js](../src/script/main.js#L421-L432))
79+
80+
Never attempt partial DOM updates to existing viewers - always rebuild.
81+
82+
### Query Support Pattern
83+
- GetFeatureInfo links use `data-query-link="true"` attribute for identification
84+
- Toggle queries by adding/removing `<map-link rel="query">` elements ([main.js](../src/script/main.js#L566-L610))
85+
- Format selection affects `INFO_FORMAT` parameter in query template URL
86+
87+
### License & Legend Links
88+
- License links extracted from WMS `<Attribution>` elements, with service-level fallback ([main.js](../src/script/main.js#L124-L135))
89+
- Legend links added before `<map-extent>` per MapML spec; only first legend per style used ([main.js](../src/script/main.js#L960-L978))
90+
91+
## Critical Implementation Details
92+
93+
### Template URL Construction
94+
Build WMS URLs manually as strings to preserve MapML template variables like `{xmin}`, `{w}`, etc.:
95+
```javascript
96+
let tref = `${currentWmsBaseUrl}?SERVICE=WMS&VERSION=${version}&REQUEST=GetMap&LAYERS=${encodeURIComponent(layer.name)}&WIDTH={w}&HEIGHT={h}...`;
97+
// DON'T use URLSearchParams - it will encode the curly braces
98+
```
99+
100+
### Basemap Layer Configuration
101+
Each projection has specific basemap configuration with zoom inputs, tile matrix inputs, and tile URLs ([main.js](../src/script/main.js#L640-L764)). OSMTILE uses dual tile links for geometry and labels. WGS84 has no basemap currently.
102+
103+
### GetFeatureInfo Coordinate Parameters
104+
Map click coordinates use different parameter names:
105+
- WMS 1.3.0: `&I={i}&J={j}`
106+
- WMS 1.1.1: `&X={i}&Y={j}`
107+
108+
## Common Pitfalls
109+
110+
1. **Don't modify existing viewer DOM**: Always remove and recreate viewers when changing configuration
111+
2. **BBOX ordering matters**: WMS 1.3.0 + EPSG:4326 is the ONLY case requiring lat,lon order
112+
3. **Encode layer names**: Use `encodeURIComponent(layer.name)` in URL construction
113+
4. **Root CRS inheritance**: Layers inherit CRS from root `<Capability><Layer>` ([main.js](../src/script/main.js#L148-L153))
114+
5. **Map-input attributes**: Location inputs should NOT have min/max attributes ([main.js](../src/script/main.js#L863-L870))
115+
116+
## File Organization
117+
- [src/index.html](../src/index.html): Entry point with MapML polyfill import
118+
- [src/script/main.js](../src/script/main.js): All application logic (1027 lines)
119+
- [src/style/main.css](../src/style/main.css): Styling for UI controls and map viewers
120+
- [src/capabilities.txt](../src/capabilities.txt): Preset WMS URLs for testing
Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
---
2+
name: mapml-a-markup
3+
description: Tells you how to correctly create and edit the markup for a <map-a> element. Use it when generating MapML output markup in an HTML page, especially to wrap `<map-geometry>` content, either in whole or in part, just like how you might use `<a>` to wrap all or part of a paragraph of text in HTML.
4+
---
5+
6+
The `<map-a>` element is a proposal to extend the Web to include links between maps and locations.
7+
This element allows you to wrap parts of coordinates or entire geometries, making a link out of the location/area that is wrapped. When a feature geometry or geometry part is
8+
wrapped in a `<map-a>` element, it creates a blue outline that is 1 pixel wide around the feature (by default), that lets the user know it's a "linked feature".
9+
10+
## Attributes
11+
12+
### `href`
13+
- The URL that the wrapped location points to. Note - If the `type` of the `<map-a>` is text/mapml
14+
you can provide fragments, more on fragments below.
15+
16+
---
17+
18+
### `target`
19+
- This is where the linked URL will be displayed. See table below for more details.
20+
- Defaults to `_self`, in the absence of a valid value.
21+
22+
---
23+
24+
### `type`
25+
- This is the mime type of the linked URL's format. Options are `text/html` & `text/mapml`.
26+
- Defaults to `text/mapml`, in the absence of a valid type value.
27+
28+
---
29+
30+
### `inplace`
31+
- The `inplace` attribute is a boolean attribute - `<map-a inplace href="..."><map-a>`
32+
- When present, the default view-changing behavior is overridden and the map view does not change.
33+
34+
---
35+
36+
## Target Behavior for `text/mapml`
37+
38+
| Target Value | Behavior |
39+
|-------------- |------------------------------------------------------- |
40+
| _self | Replaces the current layer with the linked URL layer. |
41+
| _blank | Adds the linked URL layer to the map. |
42+
| _parent | Replace all the layers with the linked URL layer. |
43+
| _top | Navigate the webpage to the linked URL. |
44+
45+
---
46+
47+
## Target Behavior for `text/html`
48+
49+
| Target Value | Behavior |
50+
|-------------- |----------------------------------------- |
51+
| _self | Navigate the webpage to the linked URL. |
52+
| _blank | Open the linked URL in a new tab. |
53+
| _parent | Navigate the webpage to the linked URL. |
54+
| _top | Navigate the webpage to the linked URL. |
55+
56+
---
57+
58+
## Location fragments
59+
60+
If the `type` attribute's value is `text/mapml`, you have the ability add a location fragment
61+
to the URL. This will pan & zoom the map to the given location.
62+
63+
Fragments are in the following format `#zoom, longitude, latitude`.
64+
65+
URL's solely defined in terms of location fragments pan and zoom the map to the given location regardless of the target value.
66+
i.e. `<map-a href="#1, 20, 30">...</map-a>` will pan to latitude: 30, longitude: 20 and zoom to level 1.
67+
68+
---
69+
70+
## Examples
71+
72+
### Styling Linked Features
73+
74+
To style linked features simply target the `map-a` class in your CSS, once a link is clicked you can target the
75+
`map-a-visited` class. See the example below:
76+
77+
```html
78+
<map-layer>
79+
<map-style>
80+
.map-a {
81+
stroke: red;
82+
}
83+
.map-a-visited {
84+
stroke: green;
85+
}
86+
</map-style>
87+
<map-feature>
88+
<map-properties>
89+
<h1>Basic</h1>
90+
</map-properties>
91+
<map-geometry>
92+
<map-a href="../externalMapML.mapml#2,-98,37">
93+
<map-polygon>
94+
<map-coordinates>2771 3106 2946 3113 2954 3210 2815 3192 2771 3106</map-coordinates>
95+
</map-polygon>
96+
</map-a>
97+
</map-geometry>
98+
</map-feature>
99+
</map-layer>
100+
```
101+
102+
### Wrapping a Feature Type + Location Fragment
103+
104+
```html
105+
<map-feature>
106+
<map-properties>
107+
<h1>Basic</h1>
108+
</map-properties>
109+
<map-geometry>
110+
<map-a href="../externalMapML.mapml#2,-98,37">
111+
<map-polygon>
112+
<map-coordinates>2771 3106 2946 3113 2954 3210 2815 3192 2771 3106</map-coordinates>
113+
</map-polygon>
114+
</map-a>
115+
</map-geometry>
116+
</map-feature>
117+
```
118+
119+
This will replace the current layer with the layer within externalMapML.mapml, once it's added the map will then goto
120+
zoomlevel: 2, longitude: -98, latitude: 37.
121+
122+
### Wrapping a point coordinate with `target="_blank"`
123+
124+
```html
125+
<map-feature>
126+
<map-properties>
127+
<h1>_blank target</h1>
128+
</map-properties>
129+
<map-geometry>
130+
<map-polygon>
131+
<map-coordinates>2771 3106 2946 3113 <map-a href="file.mapml" target="_blank"> 2954 3210 </map-a> 2815 3192 2771 3106</map-coordinates>
132+
</map-polygon>
133+
</map-geometry>
134+
</map-feature>
135+
```
136+
137+
In this example, a point will be created at (2954, 3210) which, once clicked, adds a new layer to the map.
138+
139+
### Nested `<map-a>` definition and behavior
140+
141+
```html
142+
<map-feature>
143+
<map-properties>
144+
<h1>Advanced Example</h1>
145+
</map-properties>
146+
<map-geometry>
147+
<map-a href="parent.mapml" target="_blank">
148+
<map-multipolygon>
149+
<map-polygon>
150+
<map-coordinates>2771 3106 2946 3113 <map-a href="webpage.html" target="_blank" type="text/mapml"> 2954 3210 </map-a> 2815 3192 2771 3106</map-coordinates>
151+
</map-polygon>
152+
<map-a href="nested.mapml" target="_top">
153+
<map-polygon>
154+
<map-coordinates>11 11 12 11 12 12 11 12</map-coordinates>
155+
</map-polygon>
156+
</map-a>
157+
</map-multipolygon>
158+
</map-a>
159+
</map-geometry>
160+
</map-feature>
161+
```
162+
In this advanced example there are multiple nested `<map-a>`. The simple behavior is, the closest `<map-a>` is the link
163+
behavior that the given location/area will adopt.
164+
165+
---
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
---
2+
name: mapml-caption-markup
3+
description: Tells you how to correctly create and edit the markup for a <map-caption> element. Use it when generating MapML output markup in an HTML page, especially when generating a `<mapml-viewer>` element, to describe the map's purpose, if possible and known.
4+
---
5+
6+
This element is especially important for screen reader users to understand the purpose of a map. It is like 'alt-text' for a map.
7+
8+
The `<map-caption>` element is a child of `<mapml-viewer>` and is used to define
9+
a simple text string that is not visually rendered (at this time).
10+
The caption should be read by screen readers when the `<mapml-viewer>` is focused,
11+
as it generates the `<mapml-viewer aria-label="...">` value, if no aria-label
12+
has been specified by the HTML author. `<map-caption>` may be the first or last
13+
element child of `<mapml-viewer>`.

.github/skills/map-feature-markup/SKILL.md

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,6 @@ Map features are represented in HTML MapML using a `<map-feature>` element, whic
99

1010
A `<map-feature>` element is a container for a feature's accessible name (`<map-featurecaption>`), scalar properties (`<map-properties>`) and its geometry (`<map-geometry>`). The `<map-feature>` element can be modeled as inline HTML content as a child of the `<map-layer>` element, or in an XHTML MapML document, as a child of the `<map-body>` element.
1111

12-
<iframe src="../../../demo/map-feature-demo/" title="MapML Demo" height="410" width="100%" scrolling="no" frameBorder="0"></iframe>
13-
1412
## Attributes
1513

1614
### `zoom`

.github/skills/map-geometry-markup/SKILLS.md

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,6 @@ A `<map-geometry>` element is a child of `<map-feature>` and is used to describe
77

88
A `<map-geometry>` element has one child element, which can be a `<map-point>`, `<map-linestring>`, `<map-polygon>`, `<map-multipoint>`, `<map-multilinestring>`, `<map-multipolygon>`, or `<map-geometrycollection>`.
99

10-
<iframe src="../../../demo/map-geometry-demo/" title="MapML Demo" height="410" width="100%" scrolling="no" frameBorder="0"></iframe>
11-
1210
## Attributes
1311

1412
### `cs`

.github/skills/map-input-markup/SKILL.md

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,6 @@ The `<map-input>` declares a variable that will be set and used by the polyfill
1212
according to its attributes, as the map changes viewport extent in response to
1313
user gestures.
1414

15-
<iframe src="../../../demo/map-input-demo/" title="MapML Demo" height="410" width="100%" scrolling="no" frameBorder="0"></iframe>
16-
1715
## Attributes
1816

1917
### `name`

.github/skills/map-layer-markup/SKILL.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ or fetched, from the `<map-layer src="..."></map-layer>` source attribute URL:
2121
<map-layer label="My Layer" src="https://example.org/mapml/mylayer" checked></map-layer>
2222
```
2323

24-
<iframe src="../../../demo/layer-demo/" title="MapML Demo" height="410" width="100%" scrolling="no" frameBorder="0"></iframe>
2524

2625
This documentation uses the convention of inline content mostly. Fetched map content
2726
follows similar semantics, except it is parsed with the browser's XML parser and
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
---
2+
name: mapml-properties-markup
3+
description: Tells you how to correctly create and edit the markup for a `<map-properties>` element. Use it when generating MapML output markup in an HTML page.
4+
---
5+
6+
A `<map-properties>` element is a child of `<map-feature>` and is used to define the content of the popup associated to a given feature.
7+
8+
A `<map-properties>` element can contain any HTML Element to describe the feature's content.
9+
10+
---
11+
12+
## Examples
13+
14+
### Properties Table
15+
16+
The following example displays the popup as an HTML [table](https://html.spec.whatwg.org/multipage/tables.html#the-table-element).
17+
18+
```html
19+
<mapml-viewer projection="OSMTILE" zoom="12" lat="45.42" lon="-75.70">
20+
<map-layer label="OpenStreetMap" src="../data/osm.mapml" checked></map-layer>
21+
<map-layer label="Ottawa" checked>
22+
<map-meta name="projection" content="OSMTILE"></map-meta>
23+
<map-meta name="cs" content="gcrs"></map-meta>
24+
<map-feature>
25+
<map-featurecaption>Ottawa</map-featurecaption>
26+
<map-geometry>
27+
<map-point class="ottawa">
28+
<map-coordinates>-75.697193 45.421530</map-coordinates>
29+
</map-point>
30+
</map-geometry>
31+
<map-properties>
32+
<table>
33+
<thead>
34+
<tr>
35+
<th role="columnheader" scope="col">Property name</th>
36+
<th role="columnheader" scope="col">Property value</th>
37+
</tr>
38+
</thead>
39+
<tbody>
40+
<tr>
41+
<th scope="row">Name</th>
42+
<td itemprop="amenity">Ottawa</td>
43+
</tr>
44+
<tr>
45+
<th scope="row">Type</th>
46+
<td itemprop="name">City</td>
47+
</tr>
48+
<tr>
49+
<th scope="row">Website</th>
50+
<td itemprop="website"><a href="https://ottawa.ca/" target="_blank">Ottawa</a></td>
51+
</tr>
52+
</tbody>
53+
</table>
54+
</map-properties>
55+
</map-feature>
56+
</map-layer>
57+
</mapml-viewer>
58+
```
59+
60+

0 commit comments

Comments
 (0)