Skip to content

Commit ee611e3

Browse files
committed
feat(ol-search-control): add search control
closes #306
1 parent 3b3464d commit ee611e3

File tree

7 files changed

+203
-0
lines changed

7 files changed

+203
-0
lines changed

docs/.vitepress/config.ts

+4
Original file line numberDiff line numberDiff line change
@@ -377,6 +377,10 @@ export default defineConfig({
377377
text: "ol-rotate-control",
378378
link: "/componentsguide/mapcontrols/rotate/",
379379
},
380+
{
381+
text: "ol-search-control",
382+
link: "/componentsguide/mapcontrols/search/",
383+
},
380384
{
381385
text: "ol-scaleline-control",
382386
link: "/componentsguide/mapcontrols/scaleline/",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
# ol-search-control
2+
3+
> A Search control for OpenLayers.
4+
5+
<script setup>
6+
import SearchControlDemo from "@demos/SearchControlDemo.vue"
7+
</script>
8+
<ClientOnly>
9+
<SearchControlDemo />
10+
</ClientOnly>
11+
12+
## Usage
13+
14+
::: code-group
15+
16+
<<< ../../../../src/demos/SearchControlDemo.vue
17+
18+
:::
19+
20+
## Properties
21+
22+
### Props from OpenLayers
23+
24+
Properties are passed-trough from `ol-ext` directly.
25+
Their types and default values can be checked-out [in the official OpenLayers docs](https://viglino.github.io/ol-ext/examples/search/map.control.search.html).
26+
Only some properties deviate caused by reserved keywords from Vue / HTML.
27+
This deviating props are described in the section below.
28+
29+
### Deviating Properties
30+
31+
None.
32+
33+
## Events
34+
35+
### select
36+
37+
Emits when selecting an item from the list of search results.
38+
39+
- **Type**: `SearchEvent`
40+
41+
```vue
42+
<template>
43+
<ol-map ref="map" style="height: 400px">
44+
<!-- ... -->
45+
<ol-search-control :autocomplete="autocomplete" @select="select"/>
46+
</ol-map>
47+
</template>
48+
49+
<script setup lang="ts">
50+
// ...
51+
52+
function select(e: SearchEvent) {
53+
const map: Map = e.target.getMap();
54+
map.getView().animate({
55+
center: e.search.pos,
56+
zoom: e.search.zoom,
57+
easing: easeOut,
58+
});
59+
}
60+
</script>
61+
```
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<template>
2+
<div v-if="false"></div>
3+
</template>
4+
<script setup lang="ts">
5+
import Search, { type Options } from "ol-ext/control/Search";
6+
import { useAttrs } from "vue";
7+
import useControl from "@/composables/useControl";
8+
import usePropsAsObjectProperties from "@/composables/usePropsAsObjectProperties";
9+
10+
const emit = defineEmits(["select"]);
11+
12+
const props = withDefaults(defineProps<Options>(), {
13+
className: "ol-search",
14+
});
15+
16+
const attrs = useAttrs();
17+
const { properties } = usePropsAsObjectProperties(props);
18+
19+
const { control } = useControl(Search, properties, attrs);
20+
21+
control.value.on("select", (event) => emit("select", event));
22+
23+
defineExpose({ control });
24+
</script>

src/components/mapControls/index.ts

+3
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import OlOverviewMapControl from "./OlOverviewMapControl.vue";
1111
import OlPrintDialogControl from "./OlPrintDialogControl.vue";
1212
import OlRotateControl from "./OlRotateControl.vue";
1313
import OlScaleLineControl from "./OlScaleLineControl.vue";
14+
import OlSearchControl from "./OlSearchControl.vue";
1415
import OlSwipeControl from "./OlSwipeControl.vue";
1516
import OlToggleControl from "./OlToggleControl.vue";
1617
import OlVideoRecorderControl from "./OlVideoRecorderControl.vue";
@@ -30,6 +31,7 @@ function install(app: App) {
3031
app.component("ol-zoomtoextent-control", OlZoomToExtentControl);
3132
app.component("ol-rotate-control", OlRotateControl);
3233
app.component("ol-context-menu-control", OlContextMenuControl);
34+
app.component("ol-search-control", OlSearchControl);
3335
app.component("ol-swipe-control", OlSwipeControl);
3436
app.component("ol-control-bar", OlControlBar);
3537
app.component("ol-toggle-control", OlToggleControl);
@@ -57,6 +59,7 @@ export {
5759
OlPrintDialogControl,
5860
OlRotateControl,
5961
OlScaleLineControl,
62+
OlSearchControl,
6063
OlSwipeControl,
6164
OlToggleControl,
6265
OlVideoRecorderControl,

src/composables/useControl.ts

+4
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ import type { Options as LayerSwitcherOptions } from "ol-ext/control/LayerSwitch
2727
import type LayerSwitcherImage from "ol-ext/control/LayerSwitcherImage";
2828
import type Rotate from "ol/control/Rotate";
2929
import type { Options as RotateOptions } from "ol/control/Rotate";
30+
import type Search from "ol-ext/control/Search";
31+
import type { Options as SearchOptions } from "ol-ext/control/Search";
3032
import type Swipe from "ol-ext/control/Swipe";
3133
import type { Options as SwipeOptions } from "ol-ext/control/Swipe";
3234
import type Zone from "ol-ext/control/MapZone";
@@ -55,6 +57,7 @@ type InnerControlType = (
5557
| MousePosition
5658
| OverviewMap
5759
| Rotate
60+
| Search
5861
| ScaleLine
5962
| Swipe
6063
| Zone
@@ -73,6 +76,7 @@ type InnerControlProperties =
7376
| ToggleOptions
7477
| VideoRecorderOptions
7578
| LayerSwitcherOptions
79+
| SearchOptions
7680
| RotateOptions
7781
| SwipeOptions
7882
| MapZoneOptions

src/demos/SearchControlDemo.vue

+106
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
<template>
2+
<ol-map ref="map" style="height: 400px" :controls="[]">
3+
<ol-view
4+
ref="view"
5+
:center="center"
6+
:zoom="zoom"
7+
:projection="projection"
8+
/>
9+
<ol-tile-layer title="OSM">
10+
<ol-source-osm />
11+
</ol-tile-layer>
12+
13+
<ol-search-control
14+
:autocomplete="autocomplete"
15+
:getTitle="getTitle"
16+
:collapsed="true"
17+
:maxHistory="10"
18+
@select="select"
19+
/>
20+
</ol-map>
21+
</template>
22+
23+
<script setup lang="ts">
24+
import type { Coordinate } from "ol/coordinate";
25+
import { type SearchEvent } from "ol-ext/control/Search";
26+
import { easeOut } from "ol/easing";
27+
import { ref } from "vue";
28+
import type { Map } from "ol";
29+
30+
type City = {
31+
name: string;
32+
pos: Coordinate;
33+
zoom: number;
34+
};
35+
36+
const center = ref([40, 40]);
37+
const projection = ref("EPSG:4326");
38+
const zoom = ref(8);
39+
40+
const positions: City[] = [
41+
{
42+
name: "Paris",
43+
pos: [2.351828, 48.856578],
44+
zoom: 11,
45+
},
46+
{
47+
name: "London",
48+
pos: [-0.1275, 51.507222],
49+
zoom: 11,
50+
},
51+
{
52+
name: "Geneve",
53+
pos: [6.149985, 46.200013],
54+
zoom: 13,
55+
},
56+
{
57+
name: "Bruxelles",
58+
pos: [4.35, 50.83],
59+
zoom: 12,
60+
},
61+
{
62+
name: "Berlin",
63+
pos: [13.383333, 52.516667],
64+
zoom: 12,
65+
},
66+
{
67+
name: "Madrid",
68+
pos: [-3.683333, 40.433333],
69+
zoom: 12,
70+
},
71+
{
72+
name: "Roma",
73+
pos: [12.48657, 41.888732],
74+
zoom: 12,
75+
},
76+
];
77+
78+
function getTitle(f: City) {
79+
return f.name;
80+
}
81+
82+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
83+
function autocomplete(s: string, callback: (result: City[]) => void) {
84+
const result = [];
85+
for (let i = 0; i < positions.length; i++) {
86+
if (new RegExp(s.replace("*", "") || ".*", "i").test(positions[i].name)) {
87+
result.push(positions[i]);
88+
}
89+
}
90+
/* Return result directly... */
91+
return result;
92+
/* ...or use the callback function
93+
callback(result);
94+
return false;
95+
*/
96+
}
97+
98+
function select(e: SearchEvent) {
99+
const map: Map = e.target.getMap();
100+
map.getView().animate({
101+
center: e.search.pos,
102+
zoom: e.search.zoom,
103+
easing: easeOut,
104+
});
105+
}
106+
</script>

vite.config.ts

+1
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ export default defineConfig({
107107
"ol-ext/control/PrintDialog": "PrintDialog",
108108
"ol/control/Rotate": "Rotate$1",
109109
"ol-ext/control/Swipe": "Swipe",
110+
"ol-ext/control/Search": "Search",
110111
"ol-ext/control/Toggle": "Toggle",
111112
"ol-ext/control/VideoRecorder": "VideoRecorder",
112113
"ol-ext/control/MapZone": "MapZone",

0 commit comments

Comments
 (0)