Skip to content

Commit

Permalink
Merge refactor/#33-lib-get-features into feature/packaging
Browse files Browse the repository at this point in the history
  • Loading branch information
dopenguin committed Feb 19, 2024
2 parents 34c2d9f + d3cc2cc commit 1494e7d
Show file tree
Hide file tree
Showing 25 changed files with 121 additions and 67 deletions.
5 changes: 4 additions & 1 deletion packages/clients/afm/API.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,10 @@ In your HTML, a div with unique ID is required that holds the following style pr
position: relative;
"
id="polarstern"
/>
>
<!-- Optional, may use if your page does not have its own <noscript> information -->
<noscript>Please use a browser with active JavaScript to use the map client.</noscript>
</div>
```

```js
Expand Down
4 changes: 3 additions & 1 deletion packages/clients/afm/example/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,9 @@ <h2>Karte</h2>
box-shadow: 0px 0px 5px 3px rgba(255, 255, 255, 0.5);
"
id="polarstern"
></div>
>
<noscript>Bitte benutzen Sie einen Browser mit aktiviertem JavaScript, um den Kartenklienten zu nutzen.</noscript>
</div>
<h2>Beispiel für programmatische Informationsbindung</h2>
<p>Aktuelle Zoomstufe: <span id="vuex-target-zoom" /></p>
<p>Featureinformationen: <span id="vuex-target-gfi" /></p>
Expand Down
4 changes: 3 additions & 1 deletion packages/clients/afm/example/prod-example.html
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,9 @@ <h2>Karte</h2>
box-shadow: 0px 0px 5px 3px rgba(255, 255, 255, 0.5);
"
id="polarstern"
></div>
>
<noscript>Bitte benutzen Sie einen Browser mit aktiviertem JavaScript, um den Kartenklienten zu nutzen.</noscript>
</div>
<h2>Beispiel für programmatische Informationsbindung</h2>
<p>Aktuelle Zoomstufe: <span id="vuex-target-zoom" /></p>
<p>Featureinformationen: <span id="vuex-target-gfi" /></p>
Expand Down
3 changes: 1 addition & 2 deletions packages/clients/afm/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,8 @@
"API.md"
],
"scripts": {
"postversion": "npm run build:ci",
"postversion": "npm run build",
"build": "rimraf dist && nx build && cd ../../.. && npm run docs:afm",
"build:ci": "rimraf dist && nx build -l silent && cd ../../.. && npm run docs:afm",
"dev": "vite --host"
},
"devDependencies": {
Expand Down
4 changes: 4 additions & 0 deletions packages/clients/dish/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# CHANGELOG

## unpublished

- Fix: The client now properly throws an error if `queryParameters` is missing on the `searchMethod` configuration of the `dish` search.

## 1.1.1

- Fix: The marker previously disappeared on being moved/reclicked on a second feature. This issue has been resolved.
Expand Down
3 changes: 1 addition & 2 deletions packages/clients/dish/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,8 @@
"CHANGELOG.md"
],
"scripts": {
"postversion": "npm run build:ci",
"postversion": "npm run build",
"build": "rimraf dist && vite build && bash ./scripts/injectFlag.sh",
"build:ci": "rimraf dist && vite build -l silent && bash ./scripts/injectFlag.sh",
"dev": "vite --host"
},
"devDependencies": {
Expand Down
4 changes: 3 additions & 1 deletion packages/clients/dish/src/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@
</head>
<body style="width: 100%; height: 100%; overscroll-behavior: contain">
<div style="width: 100%; height: 100%">
<div id="polarstern"></div>
<div id="polarstern">
<noscript>Bitte benutzen Sie einen Browser mit aktiviertem JavaScript, um den Kartenklienten zu nutzen.</noscript>
</div>
</div>
<script type="module" src="./polar-client.ts"></script>
</body>
Expand Down
5 changes: 5 additions & 0 deletions packages/clients/dish/src/utils/navigateToDenkmal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ export function navigateToDenkmal(instance, objektId: string) {
if (!wfsConfig) {
throw new Error('Client is missing wfsConfig on DISH search method.')
}
if (!wfsConfig.queryParameters) {
throw new Error(
'Client is missing wfsConfig.queryParameters on DISH search method.'
)
}

getWfsFeatures(null, denkmaelerWfsService.url, objektId, {
...wfsConfig.queryParameters.wfsConfiguration,
Expand Down
16 changes: 16 additions & 0 deletions packages/clients/generic/API.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,22 @@ The method expects a single object with the following parameters.
| enabledPlugins | string[]? | This is a client-specific field. Since the `@polar/client-generic` client contains all existing plugins, they are activated by strings. The strings match their package names: `'address-search' \| 'attributions' \| 'draw'* \| 'export' \| 'filter'* \| 'fullscreen'* \| 'geo-location'* \| 'gfi'* \| 'icon-menu' \| 'layer-chooser'* \| 'legend' \| 'loading-indicator' \| 'pins' \| 'reverse-geocoder' \| 'scale' \| 'toast' \| 'zoom'*`. The plugins marked with * are nested in the `'icon-menu'` in this pre-layouting, hence they depend upon it being active, too. |
| modifyLayerConfiguration | ((layerConf: object[]) => object[])? | Defaults to identity function. This function is applied to the loaded layer configuration before usage. That is, the `services` can be modified by this to e.g. set parameters not supported by the service register, add additional layers, and so on. |

In your HTML, a div with unique ID (`containerId` from above) is required that holds the following style properties. Width and height can be changed as you need, but are required to be defined.

```html
<div
style="
width: 680px;
height: 420px;
position: relative;
"
id="polarstern"
>
<!-- Optional, may use if your page does not have its own <noscript> information -->
<noscript>Please use a browser with active JavaScript to use the map client.</noscript>
</div>
```

`createMap` returns a Promise of a map instance. This returned instance is required to retrieve information from the map.

The package also includes a `style.css` and an `index.html` file. The `style.css`'s relative path must, if it isn't the default value `'./style.css'`, be included in the `mapConfiguration` as follows:
Expand Down
5 changes: 4 additions & 1 deletion packages/clients/meldemichel/API.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,10 @@ A document rendering the map client could e.g. look like this:
</style>
</head>
<body>
<div id="meldemichel-map-client"></div>
<div id="meldemichel-map-client">
<!-- Optional, may use if your page does not have its own <noscript> information -->
<noscript>Please use a browser with active JavaScript to use the map client.</noscript>
</div>
<script type="module">
import meldemichelMapClient from './client-meldemichel.js'
Expand Down
3 changes: 1 addition & 2 deletions packages/clients/meldemichel/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,8 @@
"API.md"
],
"scripts": {
"postversion": "npm run build:ci",
"postversion": "npm run build",
"build": "rimraf dist && nx build && copyfiles -f src/html/**/* dist",
"build:ci": "rimraf dist && nx build -l silent && copyfiles -f src/html/**/* dist",
"dev": "vite --host"
},
"devDependencies": {
Expand Down
4 changes: 3 additions & 1 deletion packages/clients/meldemichel/src/html/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@
</head>
<body style="width: 100%; height: 100%; overscroll-behavior: contain">
<div style="width: 100%; height: 100%">
<div id="polarstern"></div>
<div id="polarstern">
<noscript>Bitte benutzen Sie einen Browser mit aktiviertem JavaScript, um den Kartenklienten zu nutzen.</noscript>
</div>
</div>
<script type="module">
import client from './client-meldemichel.js'
Expand Down
4 changes: 3 additions & 1 deletion packages/clients/meldemichel/src/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@
</head>
<body style="width: 100%; height: 100%; overscroll-behavior: contain">
<div style="width: 100%; height: 100%">
<div id="polarstern"></div>
<div id="polarstern">
<noscript>Bitte benutzen Sie einen Browser mit aktiviertem JavaScript, um den Kartenklienten zu nutzen.</noscript>
</div>
</div>
<script type="module">
import client from './polar-client.ts'
Expand Down
4 changes: 3 additions & 1 deletion packages/clients/snowbox/src/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,9 @@ <h2>🗺️ Map</h2>
<option value="de">German</option>
</select>
</label>
<div id="polarstern" class="polarstern"></div>
<div id="polarstern" class="polarstern">
<noscript>Please use a browser with active JavaScript to use the map client.</noscript>
</div>
<h2>Example for programmatic information binding</h2>
<p>
This illustrates which kind of data can be retrieved from the map client.
Expand Down
4 changes: 4 additions & 0 deletions packages/lib/getFeatures/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# CHANGELOG

## unpublished

- Breaking: `getWfsFeatures` now throws errors if required parameters on the wfs configuration are missing instead of only printing error messages on the console.

## 1.0.1

- Fix: Increase type precision of EPSG codes from `string` to `EPSG:${string}`.
Expand Down
1 change: 0 additions & 1 deletion packages/lib/getFeatures/gazetteer/parse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ export function parseGazetteerResponse(
const gmlFeatures = new DOMParser()
.parseFromString(text, 'application/xml')
.getElementsByTagName(`wfs:${memberSuffix}`)
// @ts-expect-error | This is needed as this already is a workaround because the GeoJSON reader can't read the XMLResponse from a WFS-G
const featureCollection: FeatureCollection = {
type: 'FeatureCollection',
features,
Expand Down
14 changes: 14 additions & 0 deletions packages/lib/getFeatures/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,20 @@ export interface AdditionalSearchOptions {
typeNames?: string | string[]
}

/*
* Explanation by dimension:
* First: each child resembles a query
* Second: children will be ANDed on multiple children
* Third: [key, value] where key is a property name
* Explanation by example:
* [[['a', 'b'], ['a', 'c']], [['a', 'b']]]
* becomes
* QUERY(a=b && c=d), QUERY(a=b)
* where the second query is only executed if the first doesn't fill
* maxFeatures to its limit.
*/
export type KeyValueSetArray = Array<Array<[string, string]>>

/** Adds the possibility to have a 'title' attribute in a GeoJSON Feature */
export interface PolarGeoJsonFeature extends GeoJsonFeature {
/** The projection of the coordinates of the features */
Expand Down
15 changes: 2 additions & 13 deletions packages/lib/getFeatures/wfs/buildWfsFilter.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { WfsParameters } from '../types'
import { KeyValueSetArray, WfsParameters } from '../types'

const removeLinebreaks = (s) => s.replace(/\r?\n|\r/g, '')

Expand Down Expand Up @@ -58,21 +58,10 @@ const buildWfsFilterQuery = (
* Builds filter of multiple queries from possible interpretations of inputs.
* Multiple queries are sent so that service may stop computing after
* maxFeatures has been fulfilled.
* @param inputs - Explanation by dimension.
* First: each child resembles a query
* Second: children will be ANDed on multiple children
* Third: [key, value] where key is a property name
* Explanation by example.
* [[['a', 'b'], ['a', 'c']], [['a', 'b']]]
* becomes
* QUERY(a=b && c=d), QUERY(a=b)
* where the second query is only executed if the first doesn't fill
* maxFeatures to its limit.
* @param parameters - @see WfsParameters
* @returns request xml
*/
export const buildWfsFilter = (
inputs: Array<Array<[string, string]>>,
inputs: KeyValueSetArray,
parameters: WfsParameters
) =>
removeLinebreaks(
Expand Down
4 changes: 2 additions & 2 deletions packages/lib/getFeatures/wfs/getFeatureTitleFromPattern.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ export const getFeatureTitleFromPattern = (
const keys = blocks.reduce(
(keyAccumulator, block) =>
Array.isArray(block) ? [...keyAccumulator, block[1]] : keyAccumulator,
[]
) as string[]
[] as string[]
)
const foundKeys = keys.reduce(
(sum, key) =>
typeof properties[key] !== 'undefined' && properties[key] !== ''
Expand Down
35 changes: 14 additions & 21 deletions packages/lib/getFeatures/wfs/index.ts
Original file line number Diff line number Diff line change
@@ -1,41 +1,34 @@
import { WfsParameters } from '../types'
import { KeyValueSetArray, WfsParameters } from '../types'
import { errorCheck } from '../utils/errorCheck'
import { parseWfsResponse } from './parse'
import { buildWfsFilter } from './buildWfsFilter'
import { match } from './match'

export function getWfsFeatures(
export async function getWfsFeatures(
signal: AbortSignal | null,
url: string,
inputValue: string,
parameters: WfsParameters
) {
const { fieldName, patterns, patternKeys } = parameters
// arrays OF sets OF key-value-pairs
let inputs: string[][][] = [[[]]]

if (fieldName && patterns) {
console.error(
'@polar/lib-get-features: Using both fieldName and patterns for WFS search. These are mutually exclusive. Patterns will be ignored.'
if (!fieldName && (!patterns || !patternKeys)) {
throw new Error(
'Incomplete WFS search configuration. Either "fieldName" or "patterns" and "patternKeys" are required.'
)
}

if (fieldName) {
inputs = [[[fieldName, inputValue]]]
} else if (patterns && patternKeys) {
inputs = match(patterns, patternKeys, inputValue)
} else {
if (fieldName && patterns) {
console.error(
'@polar/lib-get-features: Incomplete WFS search configuration. Either "fieldName" or "patterns" and "patternKeys" are required.'
'@polar/lib-get-features: Using both fieldName and patterns for WFS search. These are mutually exclusive. Patterns will be ignored.'
)
}
// arrays of sets of key-value-pairs
const inputs: KeyValueSetArray = fieldName
? [[[fieldName, inputValue]]]
: match(patterns, patternKeys, inputValue)

const body = buildWfsFilter(inputs, parameters)

return fetch(encodeURI(url), { signal, method: 'POST', body }).then(
(response: Response) => {
errorCheck(response)
return parseWfsResponse(response, fieldName || patterns, !fieldName)
}
)
const response = await fetch(encodeURI(url), { signal, method: 'POST', body })
errorCheck(response)
return parseWfsResponse(response, fieldName || patterns, !fieldName)
}
35 changes: 21 additions & 14 deletions packages/lib/getFeatures/wfs/match.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// code doesn't produce RegExpMatchArray where index is not set ... :|
/* eslint-disable @typescript-eslint/no-non-null-assertion */

import { KeyValueSetArray } from '../types'

export type Separator = string
export type Slot = RegExpMatchArray
export type Block = Slot | Separator
Expand Down Expand Up @@ -55,10 +57,10 @@ const sortComparableMatches = (comparableA, comparableB) => {
* 2. pattern fulfillment
*/
const sortMatches = (
matches: string[][][],
matches: KeyValueSetArray,
patterns: string[],
uninterpretedCharacters: number[]
): string[][][] => {
): KeyValueSetArray => {
const comparableMatches = matches.map((match, index) => ({
match,
uninterpreted: uninterpretedCharacters[index],
Expand All @@ -72,7 +74,7 @@ const sortMatches = (

// remove duplicates and empty matches
const known: string[] = []
const sortedFilteredMatches = sortedMatches.filter((match) => {
return sortedMatches.filter((match) => {
if (match.length === 0) {
return false
}
Expand All @@ -83,23 +85,31 @@ const sortMatches = (
known.push(asString)
return true
})

return sortedFilteredMatches
}

/**
* matches an input string to patterns
*/
export const match = (
patterns: string[],
patternKeys: Record<string, string>,
patterns: string[] | undefined,
patternKeys: Record<string, string> | undefined,
inputValue: string
): string[][][] => {
const matches: string[][][] = []
): KeyValueSetArray => {
if (!patterns) {
throw new Error(
'@polar/lib-get-features: Parameter "patterns" is missing on wfs configuration for pattern-based search.'
)
}
if (!patternKeys) {
throw new Error(
'@polar/lib-get-features: Parameter "patternKeys" is missing on wfs configuration for pattern-based search.'
)
}
const matches: KeyValueSetArray = []
const uninterpretedCharacters: number[] = []
patterns.forEach((pattern) => {
const patternBlocks = getBlocks(pattern)
const patternMapping: string[][] = []
const patternMapping: Array<[string, string]> = []
let traverseInput = inputValue

patternBlocks.forEach((block) => {
Expand All @@ -126,8 +136,5 @@ export const match = (
uninterpretedCharacters.push(traverseInput.length)
matches.push(patternMapping)
})

const sortedMatches = sortMatches(matches, patterns, uninterpretedCharacters)

return sortedMatches
return sortMatches(matches, patterns, uninterpretedCharacters)
}
1 change: 1 addition & 0 deletions pages/documentation.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
<head>
<link rel="icon" href="./assets/iceberg_icon.svg">
<link rel="stylesheet" href="./mvp.css">
<link rel="stylesheet" href="./mobile.css">

<meta charset="utf-8">
<meta name="description" content="POLAR - documentation">
Expand Down
2 changes: 1 addition & 1 deletion pages/examples/layer.html
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@

render({
name: 'Layer Chooser & Attributions & Legend',
description: 'POLAR can handle an <i>arbitrary</i> amount of switchable background and togglable subject layers. Both copyright information and layer legend previews are displayed in additional plugins. For more complex WMS layers, a sub-layer menu exists. See the <a target="_blank" href="https://static.hamburg.de/kartenclient/prod/">monument map ↗</a> for an example.',
description: 'POLAR can handle an <i>arbitrary</i> amount of switchable background and togglable subject layers. Both copyright information and layer legend previews are displayed in additional plugins. For more complex WMS layers, a sub-layer menu exists. See the <a target="_blank" href="https://efi2.schleswig-holstein.de/dish/dish_client/index.html">monument map ↗</a> for an example.',
useCases: [
'Offering background material for different user types. Some prefer aerial photography, some require a road map for map navigation.',
'Offering various browsable topical information for informative maps.',
Expand Down
Loading

0 comments on commit 1494e7d

Please sign in to comment.