Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ updates:

- package-ecosystem: "github-actions"
directory: "/"
schedule:
schedule:
interval: "monthly"
reviewers:
- "theopensystemslab/planx"
91 changes: 86 additions & 5 deletions CHANGELOG.md

Large diffs are not rendered by default.

15 changes: 9 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,17 @@

A library of [Web Components](https://developer.mozilla.org/en-US/docs/Web/Web_Components) for tasks related to addresses and planning permission in the UK built with [Lit](https://lit.dev/), [Vite](https://vitejs.dev/), and [Ordnance Survey APIs](https://developer.ordnancesurvey.co.uk/).

***Web map***
**_Web map_**

`<my-map />` is an [OpenLayers](https://openlayers.org/)-powered map to support drawing and modifying red-line boundaries. Other supported modes include: highlighting an OS Feature that intersects with a given address point; clicking to select and merge multiple OS Features into a single boundary; and displaying static point or polygon data. Events are dispatched with the calculated area and geojson representation when you change your drawing.

![chrome-capture-2022-7-16-map](https://user-images.githubusercontent.com/5132349/184860750-bf7514db-7cab-4f9c-aa32-791099ecd6cc.gif)

***Postcode search***
**_Postcode search_**

`<postcode-search />` is a [GOV.UK-styled](https://frontend.design-system.service.gov.uk/) input that validates UK postcodes using these [utility methods](https://www.npmjs.com/package/postcode). When a postcode is validated, an event is dispatched containing the sanitized string.

***Address autocomplete***
**_Address autocomplete_**

`<address-autocomplete />` fetches addresses in a given UK postcode using the [OS Places API](https://developer.ordnancesurvey.co.uk/os-places-api) and displays them using GOV.UK's [accessible-autocomplete](https://github.com/alphagov/accessible-autocomplete) component. An event is dispatched with the OS record when you select an address.

Expand All @@ -31,23 +31,25 @@ Find these components in the wild, including what we're learning through public

## Bring your own API keys

Different features rely on different APIs - namely from Ordnance Survey and Mapbox.
Different features rely on different APIs - namely from Ordnance Survey and Mapbox.

You can set keys directly as props (eg `osApiKey`) on the applicable web components or [use a proxy](https://github.com/theopensystemslab/map/blob/main/docs/how-to-use-a-proxy.md) to mask these secrets.
You can set keys directly as props (eg `osApiKey`) on the applicable web components or [use a proxy](https://github.com/theopensystemslab/map/blob/main/docs/how-to-use-a-proxy.md) to mask these secrets.

Address autocomplete utilises the OS Places API.

For the map:

- The `basemap` prop defaults to `"OSVectorTile"` which requires the OS Vector Tiles API
- Basemap `"OSRaster"` uses the OS Maps API
- Basemap `"MapboxSatellite"` requires a Mapbox Access Token with with scope `style:read`
- The `"OSM"` (OpenStreetMap) basemap is available for users without any keys, and as a fallback if any of the above basemaps fail to build
- `clickFeatures` requires the OS Features API

When using Ordnance Survey APIs:

- Update the `osCopyright` attribution prop with your license number
- Configure an optional `osProxyEndpoint` to avoid exposing your keys (set this instead of `osApiKey`)
- ** We are not currently supporting a similar proxy for Mapbox because access tokens can be restricted to specific URLs via your account
- \*\* We are not currently supporting a similar proxy for Mapbox because access tokens can be restricted to specific URLs via your account

## Running locally

Expand Down Expand Up @@ -75,6 +77,7 @@ We use [Pitsby](https://pitsby.com/) for documenting our web components. It's si
We publish this package via [NPM](https://www.npmjs.com/package/@opensystemslab/map).

To create a new release:

1. Open a new PR against `main` which bumps the package.json "version" & creates a CHANGELOG.md entry, request code review & merge on approval
1. Run `npm publish` or `npm publish --tag next` if making a pre-release (requires permissions to OSL team in NPM & access to 2-factor auth method)
1. [Draft a new release](https://github.com/theopensystemslab/map/releases) via GitHub web: tag should match version, automatically generate changenotes and link above PR, then "Publish" and set as latest version (or set as pre-release if you used `--tag next` in above command)
Expand Down
23 changes: 15 additions & 8 deletions docs/how-to-use-a-proxy.md
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
# How to: Use a MyMap & AddressAutocomplete with a proxy

## Context
Both `MyMap` and `AddressAutocomplete` can call the Ordnance Survey API directly, or via a proxy.

Both `MyMap` and `AddressAutocomplete` can call the Ordnance Survey API directly, or via a proxy.

Calling the API directly may be suitable for internal use, where exposure of API keys is not a concern, whilst calling a proxy may be more suitable for public use.

A proxy endpoint can be supplied via the `osProxyEndpoint` property on these components.

Proxies are required to complete the following actions in order to work successfully -
Proxies are required to complete the following actions in order to work successfully -

- Append a valid OS API key as a search parameter to incoming requests
- Modify outgoing response with suitable CORS / CORP headers to allow the originating site access to the returned assets

## Diagram

```mermaid
sequenceDiagram
autonumber
Expand All @@ -28,25 +30,29 @@ sequenceDiagram
```

## Examples
Please see the sample code below for how a proxy could be implemented -

Please see the sample code below for how a proxy could be implemented -

### Express

Below is an annotated example of a simple proxy using [Express](https://github.com/expressjs/express) & [http-proxy-middleware](https://github.com/chimurai/http-proxy-middleware).

**index.js**

```js
import express from "express";
import { useOrdnanceSurveyProxy } from "proxy";

const app = express()
const port = 3000
const app = express();
const port = 3000;

app.use('/proxy/ordnance-survey', useOrdnanceSurveyProxy)
app.use("/proxy/ordnance-survey", useOrdnanceSurveyProxy);

app.listen(port)
app.listen(port);
```

**proxy.js**

```js
import { createProxyMiddleware } from "http-proxy-middleware";

Expand Down Expand Up @@ -94,4 +100,5 @@ export const appendAPIKey = (fullPath, req) => {
return resultPath;
};
```
> A working and more fleshed out example (in TypeScript) can be seen [here in the PlanX API](https://github.com/theopensystemslab/planx-new/blob/production/api.planx.uk/proxy/ordnanceSurvey.ts).

> A working and more fleshed out example (in TypeScript) can be seen [here in the PlanX API](https://github.com/theopensystemslab/planx-new/blob/production/api.planx.uk/proxy/ordnanceSurvey.ts).
219 changes: 108 additions & 111 deletions index.html
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
<!DOCTYPE html>
<!doctype html>
<html lang="en">

<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>OS Web Components Sandbox</title>
<script type="module" src="./src/index.ts"></script>
<!-- OS vector tile source specifies fonts in .pbf format, which OpenLayers can't load, so make them available directly -->
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=Source+Sans+Pro:wght@300;400;600&display=swap"
rel="stylesheet" />
<!-- Examples of available style options for postcode-search & address-autocomplete -->
<!-- <style>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>OS Web Components Sandbox</title>
<script type="module" src="./src/index.ts"></script>
<!-- OS vector tile source specifies fonts in .pbf format, which OpenLayers can't load, so make them available directly -->
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Source+Sans+Pro:wght@300;400;600&display=swap"
rel="stylesheet"
/>
<!-- Examples of available style options for postcode-search & address-autocomplete -->
<!-- <style>
address-autocomplete {
--autocomplete__label__font-size: 25px;
--autocomplete__input__padding: 6px 40px 7px 12px;
Expand All @@ -37,33 +38,37 @@
--postcode__input__height: 60px;
}
</style> -->
</head>
</head>

<body style="font-family:Inter,Helvetica,sans-serif;">
<div style="display:flex;flex-direction:column;">
<h1 style="color:red;font-size:16px;">
*** This is a testing sandbox - these components are unaware of each other!***
</h1>
<div style="margin-bottom:1em">
<my-map
id="example-map"
ariaLabelOlFixedOverlay="Interactive example map"
zoom="20"
maxZoom="23"
drawMode
drawMany
drawType="Point"
basemap="MapboxSatellite"
showCentreMarker
osCopyright="© Crown copyright and database rights 2024 OS (0)100024857"
osProxyEndpoint="https://api.editor.planx.dev/proxy/ordnance-survey"
/>
</div>
<div style="margin-bottom:1em">
<postcode-search hintText="Optional hint text shows up here" id="example-postcode" />
</div>
<div style="margin-bottom:1em; background-color: white;">
<!--
<body style="font-family: Inter, Helvetica, sans-serif">
<div style="display: flex; flex-direction: column">
<h1 style="color: red; font-size: 16px">
*** This is a testing sandbox - these components are unaware of each
other!***
</h1>
<div style="margin-bottom: 1em">
<my-map
id="example-map"
ariaLabelOlFixedOverlay="Interactive example map"
zoom="20"
maxZoom="23"
drawMode
drawMany
drawType="Point"
basemap="MapboxSatellite"
showCentreMarker
osCopyright="© Crown copyright and database rights 2024 OS (0)100024857"
osProxyEndpoint="https://api.editor.planx.dev/proxy/ordnance-survey"
/>
</div>
<div style="margin-bottom: 1em">
<postcode-search
hintText="Optional hint text shows up here"
id="example-postcode"
/>
</div>
<div style="margin-bottom: 1em; background-color: white">
<!--
Examples (as of March 2022):
SE5 OHU (Southwark): default/"standard" postcode example, fetches 65 LPI addresses
SE19 1NT (Lambeth): 56 DPA addresses -> 128 LPI addresses (87 "approved"), now requires paginated fetch
Expand All @@ -73,86 +78,78 @@ <h1 style="color:red;font-size:16px;">
Example with default value (used for planx "change" & "back" button behavior):
<address-autocomplete postcode="SE5 0HU" id="example-autocomplete" initialAddress="75, COBOURG ROAD, LONDON" />
-->
<address-autocomplete postcode="SE5 0HU" id="example-autocomplete" arrowStyle="light" labelStyle="static" />
<address-autocomplete
postcode="SE5 0HU"
id="example-autocomplete"
arrowStyle="light"
labelStyle="static"
/>
</div>
</div>
</div>
<script>
// --- MAP --- //
const map = document.querySelector("my-map");
map.clipGeojsonData = {
"type": "Feature",
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
-0.128307852848053,
51.50748361634746
],
[
-0.1274388171272278,
51.50773069282454
],
[
-0.12710085879135133,
51.507243216327
],
<script>
// --- MAP --- //
const map = document.querySelector("my-map");
map.clipGeojsonData = {
type: "Feature",
geometry: {
type: "Polygon",
coordinates: [
[
-0.12802890311050416,
51.50705957656797
[-0.128307852848053, 51.50748361634746],
[-0.1274388171272278, 51.50773069282454],
[-0.12710085879135133, 51.507243216327],
[-0.12802890311050416, 51.50705957656797],
[-0.128307852848053, 51.50748361634746],
],
[
-0.128307852848053,
51.50748361634746
]
]
]
},
};

map.addEventListener("ready", (event) => {
console.log("map ready");
});
],
},
};

// applicable when drawMode is enabled
map.addEventListener("geojsonChange", ({ detail: geojson }) => {
console.debug({ geojson });
});
map.addEventListener("ready", (event) => {
console.log("map ready");
});

// applicable when showFeaturesAtPoint is enabled
map.addEventListener("featuresAreaChange", ({ detail: featuresArea }) => {
console.debug({ featuresArea });
});
map.addEventListener("featuresGeojsonChange", ({ detail: featuresGeojson }) => {
console.debug({ featuresGeojson });
});
// applicable when drawMode is enabled
map.addEventListener("geojsonChange", ({ detail: geojson }) => {
console.debug({ geojson });
});

// applicable when geojsonData is provided
map.addEventListener("geojsonDataArea", ({ detail: geojsonDataArea }) => {
console.debug({ geojsonDataArea });
});
// applicable when showFeaturesAtPoint is enabled
map.addEventListener("featuresAreaChange", ({ detail: featuresArea }) => {
console.debug({ featuresArea });
});
map.addEventListener(
"featuresGeojsonChange",
({ detail: featuresGeojson }) => {
console.debug({ featuresGeojson });
},
);

// --- POSTCODE SEARCH --- //
const search = document.querySelector("postcode-search");
// applicable when geojsonData is provided
map.addEventListener("geojsonDataArea", ({ detail: geojsonDataArea }) => {
console.debug({ geojsonDataArea });
});

search.addEventListener("postcodeChange", ({ detail }) => {
console.debug({ detail });
});
// --- POSTCODE SEARCH --- //
const search = document.querySelector("postcode-search");

// --- ADDRESS AUTOCOMPLETE --- //
const autocomplete = document.querySelector("address-autocomplete");
search.addEventListener("postcodeChange", ({ detail }) => {
console.debug({ detail });
});

autocomplete.addEventListener("ready", ({ detail: data }) => {
console.log("autocomplete ready", { data });
});
// --- ADDRESS AUTOCOMPLETE --- //
const autocomplete = document.querySelector("address-autocomplete");

autocomplete.addEventListener(
"addressSelection",
({ detail: address }) => {
console.debug({ detail: address });
}
);
</script>
</body>
autocomplete.addEventListener("ready", ({ detail: data }) => {
console.log("autocomplete ready", { data });
});

</html>
autocomplete.addEventListener(
"addressSelection",
({ detail: address }) => {
console.debug({ detail: address });
},
);
</script>
</body>
</html>
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@
"happy-dom": "^9.1.9",
"husky": "^8.0.3",
"lint-staged": "^15.2.10",
"prettier": "^3.5.3",
"prettier": "^3.6.2",
"rollup-plugin-postcss-lit": "^2.1.0",
"sass": "^1.87.0",
"typescript": "^5.8.3",
Expand Down
2 changes: 1 addition & 1 deletion pitsby.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,6 @@ module.exports = {
styles: ["./dist/map.css"],
scripts: ["./dist/component-lib.es.js", "./dist/component-lib.umd.js"],
custom: {
windowTitle: 'Docs - Place Components',
windowTitle: "Docs - Place Components",
},
};
Loading