@centia-io/mcp-server 1.0.15 → 1.0.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +72 -0
- package/centia-api.json +4 -4
- package/package.json +5 -3
- package/skills/README.md +29 -0
- package/skills/centia-auth-model/SKILL.md +88 -0
- package/skills/centia-delivery/SKILL.md +74 -0
- package/skills/centia-feature/SKILL.md +50 -0
- package/skills/centia-file-import/SKILL.md +54 -0
- package/skills/centia-json-rpc/SKILL.md +86 -0
- package/skills/centia-keyvalue/SKILL.md +52 -0
- package/skills/centia-map-styling/SKILL.md +100 -0
- package/skills/centia-openapi-docs/SKILL.md +55 -0
- package/skills/centia-privileges/SKILL.md +66 -0
- package/skills/centia-provisioning/SKILL.md +105 -0
- package/skills/centia-realtime/SKILL.md +186 -0
- package/skills/centia-runtime-sdk/SKILL.md +98 -0
- package/skills/centia-types-formats/SKILL.md +74 -0
- package/skills/danish-search/SKILL.md +224 -0
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: danish-search
|
|
3
|
+
description: Danish address and cadastral (matrikel) search component for Centia apps. Covers installation, initialization, option configuration, result handling, and integration with Centia SDK for spatial queries.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Danish Search Component — `@centia-io/danish-search`
|
|
7
|
+
|
|
8
|
+
Use this skill when building Centia.io apps that need Danish address or cadastral search.
|
|
9
|
+
|
|
10
|
+
## What it does
|
|
11
|
+
|
|
12
|
+
The component searches DAR (adgangsadresser) and Matriklen (jordstykke) via
|
|
13
|
+
GC2 Elasticsearch. It provides typeahead autocomplete with intelligent
|
|
14
|
+
cascading: street → address, ejerlav → jordstykke.
|
|
15
|
+
|
|
16
|
+
## Package
|
|
17
|
+
|
|
18
|
+
| | |
|
|
19
|
+
|---|---|
|
|
20
|
+
| **npm** | `@centia-io/danish-search` |
|
|
21
|
+
| **Entry (ESM)** | `dist/danish-search.mjs` |
|
|
22
|
+
| **Entry (CJS)** | `dist/danish-search.cjs` |
|
|
23
|
+
| **Stylesheet** | `dist/style.css` |
|
|
24
|
+
|
|
25
|
+
## Installation
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
pnpm add @centia-io/danish-search
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Source files (development)
|
|
32
|
+
|
|
33
|
+
| File | Purpose |
|
|
34
|
+
|---|---|
|
|
35
|
+
| `src/danish.js` | Search logic, Elasticsearch DSL, result handling |
|
|
36
|
+
| `src/autocomplete.js` | Generic typeahead/autocomplete UI class |
|
|
37
|
+
| `src/style.css` | Standalone styling (no framework dependencies) |
|
|
38
|
+
| `src/main.js` | Demo entry point (not published) |
|
|
39
|
+
|
|
40
|
+
## Hard rules
|
|
41
|
+
|
|
42
|
+
- Do not add jQuery, Bootstrap, or other framework dependencies.
|
|
43
|
+
- Do not perform spatial/SQL queries inside the component — emit data and let the consuming app query Centia.io.
|
|
44
|
+
- Do not hardcode host or database — always pass via options.
|
|
45
|
+
|
|
46
|
+
## Initialization
|
|
47
|
+
|
|
48
|
+
```js
|
|
49
|
+
import danish from "@centia-io/danish-search";
|
|
50
|
+
import "@centia-io/danish-search/style.css";
|
|
51
|
+
|
|
52
|
+
const inputEl = danish({
|
|
53
|
+
el: ".custom-search", // CSS selector for input element (default: ".custom-search")
|
|
54
|
+
host: "https://dk.gc2.io", // GC2 host (default)
|
|
55
|
+
db: "dk", // GC2 database (default)
|
|
56
|
+
onlyAddress: false, // true = hide matrikel results
|
|
57
|
+
komKode: "*", // municipality filter: "*", "101", or ["101","147"]
|
|
58
|
+
size: 20, // max results per query
|
|
59
|
+
onSelect({ type, gid, value, searchType }) {
|
|
60
|
+
// Called when a final result is selected
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Returns the input `HTMLElement`.
|
|
66
|
+
|
|
67
|
+
## Options reference
|
|
68
|
+
|
|
69
|
+
| Option | Type | Default | Description |
|
|
70
|
+
|---|---|---|---|
|
|
71
|
+
| `el` | `string` | `".custom-search"` | CSS selector for the search input |
|
|
72
|
+
| `host` | `string` | `"https://dk.gc2.io"` | GC2 Elasticsearch host |
|
|
73
|
+
| `db` | `string` | `"dk"` | GC2 database name |
|
|
74
|
+
| `onlyAddress` | `boolean` | `false` | Suppress matrikel results |
|
|
75
|
+
| `komKode` | `string \| string[]` | `"*"` | Municipality code(s), `"*"` = all |
|
|
76
|
+
| `size` | `number` | `20` | Max suggestions per category |
|
|
77
|
+
| `onSelect` | `function` | — | Callback on final selection |
|
|
78
|
+
|
|
79
|
+
## Result object (`onSelect` / `search:select` event detail)
|
|
80
|
+
|
|
81
|
+
```ts
|
|
82
|
+
{
|
|
83
|
+
type: "adresse" | "matrikel", // Which dataset the result came from
|
|
84
|
+
gid: string, // Unique ID (DAR id or matrikel gid)
|
|
85
|
+
value: string, // Display string shown to user
|
|
86
|
+
searchType: string, // Internal search phase (adresse, jordstykke, etc.)
|
|
87
|
+
feature: GeoJSON.Feature // The full GeoJSON feature of a adresse or matrikel
|
|
88
|
+
}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## Listening via event instead of callback
|
|
92
|
+
|
|
93
|
+
```js
|
|
94
|
+
import danish from "@centia-io/danish-search";
|
|
95
|
+
|
|
96
|
+
const inputEl = danish({ el: "#my-input" });
|
|
97
|
+
|
|
98
|
+
inputEl.addEventListener("search:select", (e) => {
|
|
99
|
+
const { type, gid, value } = e.detail;
|
|
100
|
+
// handle result
|
|
101
|
+
});
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## Integrating with Centia SDK
|
|
105
|
+
|
|
106
|
+
The `feature` in the callback is a full GeoJSON Feature (EPSG:4326) with the
|
|
107
|
+
geometry of the selected address or matrikel parcel. Use it directly for spatial
|
|
108
|
+
queries against your own data via `ST_Intersects`.
|
|
109
|
+
|
|
110
|
+
### Find rows in your own table that intersect the selected feature
|
|
111
|
+
|
|
112
|
+
```js
|
|
113
|
+
import danish from "@centia-io/danish-search";
|
|
114
|
+
import { Sql } from "@centia-io/sdk";
|
|
115
|
+
|
|
116
|
+
const sql = new Sql();
|
|
117
|
+
|
|
118
|
+
danish({
|
|
119
|
+
onSelect: async ({ feature }) => {
|
|
120
|
+
const res = await sql.exec({
|
|
121
|
+
q: `SELECT *
|
|
122
|
+
FROM my_schema.my_table
|
|
123
|
+
WHERE ST_Intersects(
|
|
124
|
+
the_geom,
|
|
125
|
+
ST_Transform(ST_SetSRID(ST_GeomFromGeoJSON(:geojson), 4326), 25832)
|
|
126
|
+
)`,
|
|
127
|
+
params: [{"geojson": feature.geometry}]
|
|
128
|
+
});
|
|
129
|
+
console.log(res.data);
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### Zoom a map to the selected feature
|
|
135
|
+
|
|
136
|
+
```js
|
|
137
|
+
danish({
|
|
138
|
+
onSelect: ({ feature }) => {
|
|
139
|
+
// Works with any map library that accepts GeoJSON (Leaflet, MapLibre, OpenLayers, etc.)
|
|
140
|
+
map.fitBounds(L.geoJSON(feature).getBounds());
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### Aggregate your data within a selected matrikel parcel
|
|
146
|
+
|
|
147
|
+
```js
|
|
148
|
+
danish({
|
|
149
|
+
onSelect: async ({ type, feature }) => {
|
|
150
|
+
if (type === "matrikel") {
|
|
151
|
+
const geojson = JSON.stringify(feature.geometry);
|
|
152
|
+
const res = await Sql.exec(`
|
|
153
|
+
SELECT count(*) AS cnt, sum(areal) AS total_areal
|
|
154
|
+
FROM my_schema.bygninger
|
|
155
|
+
WHERE ST_Intersects(
|
|
156
|
+
the_geom,
|
|
157
|
+
ST_Transform(ST_SetSRID(ST_GeomFromGeoJSON('${geojson}'), 4326), 25832)
|
|
158
|
+
)
|
|
159
|
+
`);
|
|
160
|
+
console.log(res.data);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### Use the feature properties directly
|
|
167
|
+
|
|
168
|
+
The `feature.properties` object contains all attributes from the source dataset
|
|
169
|
+
(DAR or matrikel). No extra query is needed for basic attribute display:
|
|
170
|
+
|
|
171
|
+
```js
|
|
172
|
+
danish({
|
|
173
|
+
onSelect: ({ type, feature }) => {
|
|
174
|
+
if (type === "adresse") {
|
|
175
|
+
const { postnr, postnrnavn, kommunekode } = feature.properties;
|
|
176
|
+
console.log(`${postnr} ${postnrnavn}, kommune ${kommunekode}`);
|
|
177
|
+
}
|
|
178
|
+
if (type === "matrikel") {
|
|
179
|
+
const { matrikelnummer, ejerlavsnavn } = feature.properties;
|
|
180
|
+
console.log(`${ejerlavsnavn} ${matrikelnummer}`);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
## Search cascade behavior
|
|
187
|
+
|
|
188
|
+
The component auto-narrows results:
|
|
189
|
+
|
|
190
|
+
1. **No digits, no comma, no spaces** → searches street names and city names separately
|
|
191
|
+
2. **No digits, no comma, with spaces** → searches combined street + city
|
|
192
|
+
3. **Contains comma or digits** → searches full addresses directly
|
|
193
|
+
4. **Comma with house number after city** → auto-normalizes query (e.g. `"Vej, By 35"` → `"Vej 35, By"`)
|
|
194
|
+
5. **Single result at street/city level** → auto-cascades to full address search
|
|
195
|
+
|
|
196
|
+
Matrikel follows the same pattern:
|
|
197
|
+
1. **No digits** → ejerlav (estate name) aggregation
|
|
198
|
+
2. **Contains digits** → jordstykke (parcel) hits
|
|
199
|
+
3. **Single ejerlav** → auto-cascades to jordstykke
|
|
200
|
+
|
|
201
|
+
Intermediate selections (street name, city, ejerlav) are injected back into the
|
|
202
|
+
input to narrow the search — `onSelect` only fires for final results with a GID.
|
|
203
|
+
|
|
204
|
+
## Required HTML
|
|
205
|
+
|
|
206
|
+
The component needs an `<input>` element matching the `el` selector:
|
|
207
|
+
|
|
208
|
+
```html
|
|
209
|
+
<input class="custom-search" type="text" placeholder="Søg adresse eller matrikel...">
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
Import `@centia-io/danish-search/style.css` for dropdown styling, or provide your own styles for these classes:
|
|
213
|
+
|
|
214
|
+
- `.tt-dropdown-menu` — dropdown container
|
|
215
|
+
- `.tt-suggestions` — suggestions wrapper
|
|
216
|
+
- `.tt-suggestion` — individual suggestion
|
|
217
|
+
- `.tt-suggestion.tt-cursor` — highlighted suggestion
|
|
218
|
+
- `.typeahead-heading` — category header (Adresser / Matrikel)
|
|
219
|
+
|
|
220
|
+
## Security notes
|
|
221
|
+
|
|
222
|
+
- GID values come from Elasticsearch and are used in SQL queries — always use parameterized queries or validate GIDs when building SQL for Centia.
|
|
223
|
+
- DAR `id` values are UUIDs. Matrikel `gid` values are integers.
|
|
224
|
+
- Do not expose the GC2 Elasticsearch endpoint to user-controlled input beyond the search query itself.
|