@kispace-io/gs-lib 0.0.0
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/README.md +58 -0
- package/bin/map-builder.js +132 -0
- package/dist/base-map-builder.d.ts +102 -0
- package/dist/base-map-builder.d.ts.map +1 -0
- package/dist/gs-gs2ol.d.ts +41 -0
- package/dist/gs-gs2ol.d.ts.map +1 -0
- package/dist/gs-lib.css +3724 -0
- package/dist/gs-lib.d.ts +16 -0
- package/dist/gs-lib.d.ts.map +1 -0
- package/dist/gs-litns.d.ts +32 -0
- package/dist/gs-litns.d.ts.map +1 -0
- package/dist/gs-model.d.ts +186 -0
- package/dist/gs-model.d.ts.map +1 -0
- package/dist/gs-ol-adapters.d.ts +23 -0
- package/dist/gs-ol-adapters.d.ts.map +1 -0
- package/dist/gs-ol2gs.d.ts +9 -0
- package/dist/gs-ol2gs.d.ts.map +1 -0
- package/dist/gs-olns.d.ts +22 -0
- package/dist/gs-olns.d.ts.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.html +69 -0
- package/dist/index.js +104888 -0
- package/dist/index.js.map +1 -0
- package/dist/pwa/assets/icons/192x192.png +0 -0
- package/dist/pwa/assets/icons/24x24.png +0 -0
- package/dist/pwa/assets/icons/48x48.png +0 -0
- package/dist/pwa/assets/icons/512x512.png +0 -0
- package/dist/pwa/assets/icons/icon_192.png +0 -0
- package/dist/pwa/assets/icons/icon_24.png +0 -0
- package/dist/pwa/assets/icons/icon_48.png +0 -0
- package/dist/pwa/assets/icons/icon_512.png +0 -0
- package/dist/pwa/manifest.json +54 -0
- package/dist/pwa/staticwebapp.config.json +6 -0
- package/dist/pwa/sw.js +109 -0
- package/lib/node-map-builder.ts +200 -0
- package/package.json +51 -0
- package/public/index.html +69 -0
- package/public/pwa/assets/icons/192x192.png +0 -0
- package/public/pwa/assets/icons/24x24.png +0 -0
- package/public/pwa/assets/icons/48x48.png +0 -0
- package/public/pwa/assets/icons/512x512.png +0 -0
- package/public/pwa/assets/icons/icon_192.png +0 -0
- package/public/pwa/assets/icons/icon_24.png +0 -0
- package/public/pwa/assets/icons/icon_48.png +0 -0
- package/public/pwa/assets/icons/icon_512.png +0 -0
- package/public/pwa/manifest.json +54 -0
- package/public/pwa/staticwebapp.config.json +6 -0
- package/public/pwa/sw.js +109 -0
- package/src/base-map-builder.ts +414 -0
- package/src/gs-gs2ol.ts +626 -0
- package/src/gs-lib.ts +54 -0
- package/src/gs-litns.ts +213 -0
- package/src/gs-model.ts +393 -0
- package/src/gs-ol-adapters.ts +89 -0
- package/src/gs-ol2gs.ts +86 -0
- package/src/gs-olns.ts +30 -0
- package/src/index.ts +15 -0
- package/tsconfig.json +23 -0
package/src/gs-litns.ts
ADDED
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
// Lit namespace module - exports all lit APIs for user modules
|
|
2
|
+
// Lit is required for gs-ol-adapters (uses lit.render) and for user modules
|
|
3
|
+
// WebAwesome is imported here to make it available to user modules (treated as part of lit)
|
|
4
|
+
|
|
5
|
+
// Re-export all Lit APIs from appspace's externals/lit module - single source of truth
|
|
6
|
+
export * from "@kispace-io/appspace/externals/lit-exports";
|
|
7
|
+
|
|
8
|
+
// Import specific items needed for defineComponent and lit namespace object
|
|
9
|
+
import {
|
|
10
|
+
html, css, nothing, svg, LitElement, unsafeCSS,
|
|
11
|
+
when, repeat, keyed, classMap, styleMap, ifDefined,
|
|
12
|
+
guard, cache, unsafeHTML, until, live, createRef, ref, render
|
|
13
|
+
} from "@kispace-io/appspace/externals/lit-exports";
|
|
14
|
+
|
|
15
|
+
// Import WebAwesome components from appspace's externals/webawesome module
|
|
16
|
+
// This maintains a single source of truth - Vite will optimize the build to only include what's needed
|
|
17
|
+
// WebAwesome is treated as part of lit, so it's always available to user modules
|
|
18
|
+
import "@kispace-io/appspace/externals/webawesome";
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Helper function to define a custom web component from a simple function.
|
|
22
|
+
* Converts a render function into a LitElement-based web component.
|
|
23
|
+
* Properties are automatically inferred from the props used in the render function.
|
|
24
|
+
*
|
|
25
|
+
* @param tagName - The custom element tag name (must contain a hyphen)
|
|
26
|
+
* @param renderFn - Function that receives props and returns a Lit template
|
|
27
|
+
* @param options - Optional configuration (props defaults, styles, etc.)
|
|
28
|
+
* @returns The tag name of the registered component
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* ```javascript
|
|
32
|
+
* export default function ({lit: {html, css, defineComponent}}) {
|
|
33
|
+
* // Simple component - properties inferred from props usage
|
|
34
|
+
* defineComponent('my-button', (props) => html`
|
|
35
|
+
* <button @click=${props.onClick}>${props.label || 'Click'}</button>
|
|
36
|
+
* `);
|
|
37
|
+
*
|
|
38
|
+
* // Component with default props (auto-converted to properties)
|
|
39
|
+
* defineComponent('my-counter', (props) => html`
|
|
40
|
+
* <div class="counter">Count: ${props.count}</div>
|
|
41
|
+
* `, {
|
|
42
|
+
* props: { count: 0, label: '' }, // Simple object - auto-converted
|
|
43
|
+
* styles: css`
|
|
44
|
+
* .counter { font-weight: bold; }
|
|
45
|
+
* `
|
|
46
|
+
* });
|
|
47
|
+
*
|
|
48
|
+
* // Use the component in your template
|
|
49
|
+
* return () => html`
|
|
50
|
+
* <my-counter count="5"></my-counter>
|
|
51
|
+
* `;
|
|
52
|
+
* }
|
|
53
|
+
* ```
|
|
54
|
+
*/
|
|
55
|
+
const defineComponent = (
|
|
56
|
+
tagName: string,
|
|
57
|
+
renderFn: (props: any) => any,
|
|
58
|
+
options?: {
|
|
59
|
+
props?: Record<string, any> | Record<string, { type?: any; reflect?: boolean; attribute?: string | boolean }>;
|
|
60
|
+
styles?: any;
|
|
61
|
+
shadowRootOptions?: ShadowRootInit;
|
|
62
|
+
}
|
|
63
|
+
): string => {
|
|
64
|
+
if (!tagName.includes('-')) {
|
|
65
|
+
throw new Error(`Custom element tag name must contain a hyphen: ${tagName}`);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Check if already defined
|
|
69
|
+
if (customElements.get(tagName)) {
|
|
70
|
+
return tagName;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Convert simple props object to LitElement property definitions
|
|
74
|
+
const convertPropsToProperties = (props: Record<string, any>): Record<string, any> => {
|
|
75
|
+
const properties: Record<string, any> = {};
|
|
76
|
+
|
|
77
|
+
for (const [key, value] of Object.entries(props)) {
|
|
78
|
+
// If it's already a LitElement property config, use it as-is
|
|
79
|
+
if (value && typeof value === 'object' && ('type' in value || 'reflect' in value || 'attribute' in value)) {
|
|
80
|
+
properties[key] = value;
|
|
81
|
+
} else {
|
|
82
|
+
// Infer type from default value
|
|
83
|
+
const type = value === null || value === undefined ? String
|
|
84
|
+
: typeof value === 'boolean' ? Boolean
|
|
85
|
+
: typeof value === 'number' ? Number
|
|
86
|
+
: typeof value === 'object' ? Object
|
|
87
|
+
: String;
|
|
88
|
+
|
|
89
|
+
properties[key] = {
|
|
90
|
+
type: type,
|
|
91
|
+
// Set default value
|
|
92
|
+
...(value !== undefined && { value })
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return properties;
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
// Determine properties from options
|
|
101
|
+
let properties: Record<string, any> = {};
|
|
102
|
+
if (options?.props) {
|
|
103
|
+
properties = convertPropsToProperties(options.props);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Track props accessed during render to auto-create properties
|
|
107
|
+
// This allows properties to be inferred from render function usage
|
|
108
|
+
const accessedProps = new Set<string>();
|
|
109
|
+
const propsProxy = new Proxy({ $this: {} }, {
|
|
110
|
+
get(_target, prop: string) {
|
|
111
|
+
// Track all property accesses except built-ins and $this
|
|
112
|
+
if (typeof prop === 'string' &&
|
|
113
|
+
prop !== '$this' &&
|
|
114
|
+
prop !== 'toJSON' &&
|
|
115
|
+
prop !== 'toString' &&
|
|
116
|
+
prop !== 'valueOf' &&
|
|
117
|
+
!prop.startsWith('Symbol.')) {
|
|
118
|
+
accessedProps.add(prop);
|
|
119
|
+
}
|
|
120
|
+
// Return a safe default value based on common patterns
|
|
121
|
+
return prop.endsWith('Handler') || prop.startsWith('on') ? (() => {}) : '';
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
// Do an initial render to detect accessed props
|
|
126
|
+
// This is safe because we catch all errors and the render function
|
|
127
|
+
// should be pure (no side effects)
|
|
128
|
+
try {
|
|
129
|
+
renderFn(propsProxy);
|
|
130
|
+
} catch {
|
|
131
|
+
// Ignore errors during prop detection - render function might
|
|
132
|
+
// throw if props are required, but that's okay for detection
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Add auto-detected props that weren't explicitly defined
|
|
136
|
+
// Default to String type for auto-detected props
|
|
137
|
+
for (const prop of accessedProps) {
|
|
138
|
+
if (!(prop in properties)) {
|
|
139
|
+
properties[prop] = { type: String };
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
class DynamicComponent extends LitElement {
|
|
144
|
+
static properties = properties;
|
|
145
|
+
|
|
146
|
+
static styles = options?.styles;
|
|
147
|
+
|
|
148
|
+
constructor() {
|
|
149
|
+
super();
|
|
150
|
+
// Initialize default values from simple props (not LitElement config objects)
|
|
151
|
+
if (options?.props) {
|
|
152
|
+
for (const [key, value] of Object.entries(options.props)) {
|
|
153
|
+
// Only set default if it's a simple value, not a LitElement property config
|
|
154
|
+
if (value !== undefined && !(value && typeof value === 'object' && ('type' in value || 'reflect' in value || 'attribute' in value))) {
|
|
155
|
+
(this as any)[key] = value;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
createRenderRoot() {
|
|
162
|
+
if (options?.shadowRootOptions) {
|
|
163
|
+
return this.attachShadow(options.shadowRootOptions);
|
|
164
|
+
}
|
|
165
|
+
return super.createRenderRoot();
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
render() {
|
|
169
|
+
// Create a props object from element properties
|
|
170
|
+
const props: any = {
|
|
171
|
+
$this: this // Element instance - needed for arrow functions which don't have 'this'
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
// Copy all properties to props object
|
|
175
|
+
for (const key of Object.keys(properties)) {
|
|
176
|
+
props[key] = (this as any)[key];
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Bind render function to this element so 'this' refers to the element instance
|
|
180
|
+
// Note: 'this' works for regular functions, 'props.$this' works for arrow functions
|
|
181
|
+
return renderFn.call(this, props);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Register the custom element
|
|
186
|
+
customElements.define(tagName, DynamicComponent);
|
|
187
|
+
|
|
188
|
+
return tagName;
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
// Export all lit APIs as a namespace object
|
|
192
|
+
export const lit = {
|
|
193
|
+
html,
|
|
194
|
+
css,
|
|
195
|
+
unsafeCSS,
|
|
196
|
+
svg,
|
|
197
|
+
nothing,
|
|
198
|
+
when,
|
|
199
|
+
repeat,
|
|
200
|
+
keyed,
|
|
201
|
+
classMap,
|
|
202
|
+
styleMap,
|
|
203
|
+
ifDefined,
|
|
204
|
+
guard,
|
|
205
|
+
cache,
|
|
206
|
+
unsafeHTML,
|
|
207
|
+
until,
|
|
208
|
+
live,
|
|
209
|
+
ref,
|
|
210
|
+
createRef,
|
|
211
|
+
defineComponent
|
|
212
|
+
};
|
|
213
|
+
|
package/src/gs-model.ts
ADDED
|
@@ -0,0 +1,393 @@
|
|
|
1
|
+
import { v4 as uuidv4 } from '@kispace-io/appspace/externals/third-party'
|
|
2
|
+
|
|
3
|
+
export const KEY_LABEL = "label";
|
|
4
|
+
export const KEY_NAME = "name";
|
|
5
|
+
export const KEY_URL = "url";
|
|
6
|
+
export const KEY_FORMAT = "format";
|
|
7
|
+
export const KEY_ICON_PATH = "iconPath";
|
|
8
|
+
export const KEY_STATE = "_state";
|
|
9
|
+
export const KEY_SRC = "src";
|
|
10
|
+
export const KEY_SOURCETYPE = "sourceType"
|
|
11
|
+
export const KEY_ENV = "_env"
|
|
12
|
+
export const KEY_GS_MANAGED = "gsManaged"
|
|
13
|
+
export const KEY_SETTINGS = "settings"
|
|
14
|
+
export const KEY_UUID = "uuid"
|
|
15
|
+
export const KEY_EVENT_SUBSCRIPTIONS = "_eventSubscriptions"
|
|
16
|
+
|
|
17
|
+
export const LAYER_GEOCODED_MARKERS = "geocoded-markers"
|
|
18
|
+
|
|
19
|
+
export interface GsBag {
|
|
20
|
+
[key: string]: any
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function ensureUuid<T extends GsState>(obj: T): T {
|
|
24
|
+
if (!obj.uuid) {
|
|
25
|
+
obj.uuid = uuidv4()
|
|
26
|
+
}
|
|
27
|
+
return obj
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function ensureUuidsRecursive<T extends GsState>(obj: T): T {
|
|
31
|
+
ensureUuid(obj)
|
|
32
|
+
|
|
33
|
+
if ('geometry' in obj && (obj as any).geometry) {
|
|
34
|
+
ensureUuid((obj as any).geometry)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if ('source' in obj && (obj as any).source) {
|
|
38
|
+
const source = (obj as any).source
|
|
39
|
+
ensureUuid(source)
|
|
40
|
+
if (source.features && Array.isArray(source.features)) {
|
|
41
|
+
source.features.forEach((feature: any) => ensureUuidsRecursive(feature))
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if ('layers' in obj && Array.isArray((obj as any).layers)) {
|
|
46
|
+
(obj as any).layers.forEach((layer: any) => ensureUuidsRecursive(layer))
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if ('overlays' in obj && Array.isArray((obj as any).overlays)) {
|
|
50
|
+
(obj as any).overlays.forEach((overlay: any) => ensureUuid(overlay))
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if ('controls' in obj && Array.isArray((obj as any).controls)) {
|
|
54
|
+
(obj as any).controls.forEach((control: any) => ensureUuid(control))
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if ('interactions' in obj && Array.isArray((obj as any).interactions)) {
|
|
58
|
+
(obj as any).interactions.forEach((interaction: any) => ensureUuid(interaction))
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if ('view' in obj && (obj as any).view) {
|
|
62
|
+
ensureUuid((obj as any).view)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return obj
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export interface GsState {
|
|
69
|
+
uuid?: string
|
|
70
|
+
state?: GsBag
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export enum GsSourceType {
|
|
74
|
+
OSM = "OSM", GeoJSON = "GeoJSON", Features = "Features", KML = "KML", GeoTIFF = "GeoTIFF", GPX = "GPX", BM = "BM", WMS = "WMS", WMTS = "WMTS", XYZ = "XYZ"
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export enum GsLayerType {
|
|
78
|
+
TILE = "TILE", VECTOR = "VECTOR", GROUP = "GROUP"
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export enum GsGeometryType {
|
|
82
|
+
Point = "Point",
|
|
83
|
+
MultiPoint = "MultiPoint",
|
|
84
|
+
Polygon = "Polygon",
|
|
85
|
+
MultiPolygon = "MultiPolygon",
|
|
86
|
+
LineString = "LineString",
|
|
87
|
+
MultiLineString = "MultiLineString",
|
|
88
|
+
Circle = "Circle",
|
|
89
|
+
LinearRing = "LinearRing"
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export interface GsGeometry extends GsState {
|
|
93
|
+
type: GsGeometryType,
|
|
94
|
+
coordinates: number[]
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export interface GsResource {
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export interface GsIcon extends GsResource {
|
|
101
|
+
src: string
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export interface GsFeature extends GsState {
|
|
105
|
+
geometry: GsGeometry
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export interface GsSource extends GsState {
|
|
109
|
+
type: GsSourceType
|
|
110
|
+
url?: string
|
|
111
|
+
features?: GsFeature[]
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export interface GsLayer extends GsState {
|
|
115
|
+
name?: string
|
|
116
|
+
type: GsLayerType,
|
|
117
|
+
source: GsSource,
|
|
118
|
+
visible?: boolean
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export interface GsScript extends GsState {
|
|
122
|
+
src: string,
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export interface GsOverlay extends GsScript {
|
|
126
|
+
position: "bottom-left" | "bottom-center" | "bottom-right" | "center-left" | "center-center" | "center-right" | "top-left" | "top-center" | "top-right",
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export interface GsControl extends GsScript {
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export interface GsInteraction extends GsState {
|
|
133
|
+
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export interface GsView extends GsState {
|
|
137
|
+
center: number[],
|
|
138
|
+
zoom: number,
|
|
139
|
+
projection: string
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export interface GsStrokeStyle {
|
|
143
|
+
color?: string
|
|
144
|
+
width?: number
|
|
145
|
+
lineDash?: number[]
|
|
146
|
+
lineCap?: 'butt' | 'round' | 'square'
|
|
147
|
+
lineJoin?: 'bevel' | 'round' | 'miter'
|
|
148
|
+
miterLimit?: number
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export interface GsFillStyle {
|
|
152
|
+
color?: string
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export interface GsImageStyle {
|
|
156
|
+
type: 'circle' | 'icon' | 'regular-shape'
|
|
157
|
+
|
|
158
|
+
radius?: number
|
|
159
|
+
fill?: GsFillStyle
|
|
160
|
+
stroke?: GsStrokeStyle
|
|
161
|
+
|
|
162
|
+
src?: string
|
|
163
|
+
scale?: number
|
|
164
|
+
anchor?: [number, number]
|
|
165
|
+
anchorXUnits?: 'fraction' | 'pixels'
|
|
166
|
+
anchorYUnits?: 'fraction' | 'pixels'
|
|
167
|
+
anchorOrigin?: 'bottom-left' | 'bottom-right' | 'top-left' | 'top-right'
|
|
168
|
+
color?: string
|
|
169
|
+
crossOrigin?: string
|
|
170
|
+
offset?: [number, number]
|
|
171
|
+
offsetOrigin?: 'bottom-left' | 'bottom-right' | 'top-left' | 'top-right'
|
|
172
|
+
size?: [number, number]
|
|
173
|
+
|
|
174
|
+
points?: number
|
|
175
|
+
radius1?: number
|
|
176
|
+
radius2?: number
|
|
177
|
+
angle?: number
|
|
178
|
+
|
|
179
|
+
opacity?: number
|
|
180
|
+
rotation?: number
|
|
181
|
+
rotateWithView?: boolean
|
|
182
|
+
displacement?: [number, number]
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
export interface GsTextStyle {
|
|
186
|
+
text?: string
|
|
187
|
+
font?: string
|
|
188
|
+
maxAngle?: number
|
|
189
|
+
offsetX?: number
|
|
190
|
+
offsetY?: number
|
|
191
|
+
overflow?: boolean
|
|
192
|
+
placement?: 'point' | 'line'
|
|
193
|
+
repeat?: number
|
|
194
|
+
scale?: number
|
|
195
|
+
rotateWithView?: boolean
|
|
196
|
+
rotation?: number
|
|
197
|
+
textAlign?: 'left' | 'center' | 'right' | 'end' | 'start'
|
|
198
|
+
justify?: 'left' | 'center' | 'right'
|
|
199
|
+
textBaseline?: 'bottom' | 'top' | 'middle' | 'alphabetic' | 'hanging' | 'ideographic'
|
|
200
|
+
fill?: GsFillStyle
|
|
201
|
+
stroke?: GsStrokeStyle
|
|
202
|
+
backgroundFill?: GsFillStyle
|
|
203
|
+
backgroundStroke?: GsStrokeStyle
|
|
204
|
+
padding?: [number, number, number, number]
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
export interface GsStyle {
|
|
208
|
+
id?: string
|
|
209
|
+
stroke?: GsStrokeStyle
|
|
210
|
+
fill?: GsFillStyle
|
|
211
|
+
image?: GsImageStyle
|
|
212
|
+
text?: GsTextStyle
|
|
213
|
+
zIndex?: number
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
export interface GsStylesMap {
|
|
217
|
+
[key: string]: GsStyle
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
export const DEFAULT_STYLES: GsStylesMap = {
|
|
221
|
+
'default-point': {
|
|
222
|
+
id: 'default-point',
|
|
223
|
+
image: {
|
|
224
|
+
type: 'circle',
|
|
225
|
+
radius: 5,
|
|
226
|
+
fill: { color: 'rgba(0, 100, 255, 0.8)' },
|
|
227
|
+
stroke: { color: 'white', width: 2 }
|
|
228
|
+
}
|
|
229
|
+
},
|
|
230
|
+
'default-line': {
|
|
231
|
+
id: 'default-line',
|
|
232
|
+
stroke: {
|
|
233
|
+
color: 'rgba(0, 100, 255, 0.8)',
|
|
234
|
+
width: 2
|
|
235
|
+
}
|
|
236
|
+
},
|
|
237
|
+
'default-polygon': {
|
|
238
|
+
id: 'default-polygon',
|
|
239
|
+
fill: { color: 'rgba(0, 100, 255, 0.3)' },
|
|
240
|
+
stroke: {
|
|
241
|
+
color: 'rgba(0, 100, 255, 0.8)',
|
|
242
|
+
width: 2
|
|
243
|
+
}
|
|
244
|
+
},
|
|
245
|
+
'selection': {
|
|
246
|
+
id: 'selection',
|
|
247
|
+
image: {
|
|
248
|
+
type: 'circle',
|
|
249
|
+
radius: 7,
|
|
250
|
+
fill: { color: 'rgba(255, 255, 0, 0.3)' },
|
|
251
|
+
stroke: { color: 'rgba(255, 255, 0, 1)', width: 3 }
|
|
252
|
+
},
|
|
253
|
+
stroke: {
|
|
254
|
+
color: 'rgba(255, 255, 0, 1)',
|
|
255
|
+
width: 3
|
|
256
|
+
},
|
|
257
|
+
fill: {
|
|
258
|
+
color: 'rgba(255, 255, 0, 0.3)'
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
export interface GsStyleRule {
|
|
264
|
+
id?: string
|
|
265
|
+
condition: {
|
|
266
|
+
geometryType?: GsGeometryType | GsGeometryType[]
|
|
267
|
+
layerName?: string
|
|
268
|
+
property?: {
|
|
269
|
+
key: string
|
|
270
|
+
value?: any
|
|
271
|
+
operator?: 'equals' | 'not-equals' | 'contains' | 'greater-than' | 'less-than' | 'exists'
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
styleId: string
|
|
275
|
+
priority?: number
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
export function evaluateStyleRule(rule: GsStyleRule, feature: GsFeature, layerName?: string): boolean {
|
|
279
|
+
const condition = rule.condition
|
|
280
|
+
|
|
281
|
+
if (condition.geometryType) {
|
|
282
|
+
const types = Array.isArray(condition.geometryType) ? condition.geometryType : [condition.geometryType]
|
|
283
|
+
if (!types.includes(feature.geometry.type)) {
|
|
284
|
+
return false
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
if (condition.layerName && condition.layerName !== layerName) {
|
|
289
|
+
return false
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
if (condition.property) {
|
|
293
|
+
const prop = condition.property
|
|
294
|
+
const featureValue = feature.state?.[prop.key]
|
|
295
|
+
|
|
296
|
+
if (!prop.operator || prop.operator === 'exists') {
|
|
297
|
+
return featureValue !== undefined
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
if (prop.operator === 'equals') {
|
|
301
|
+
return featureValue === prop.value
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
if (prop.operator === 'not-equals') {
|
|
305
|
+
return featureValue !== prop.value
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
if (prop.operator === 'contains' && typeof featureValue === 'string' && typeof prop.value === 'string') {
|
|
309
|
+
return featureValue.includes(prop.value)
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
if (prop.operator === 'greater-than' && typeof featureValue === 'number' && typeof prop.value === 'number') {
|
|
313
|
+
return featureValue > prop.value
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
if (prop.operator === 'less-than' && typeof featureValue === 'number' && typeof prop.value === 'number') {
|
|
317
|
+
return featureValue < prop.value
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
return true
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
export function getStyleForFeature(feature: GsFeature, rules: GsStyleRule[], stylesMap: GsStylesMap, layerName?: string): GsStyle | undefined {
|
|
325
|
+
const sortedRules = [...rules].sort((a, b) => (b.priority || 0) - (a.priority || 0))
|
|
326
|
+
|
|
327
|
+
for (const rule of sortedRules) {
|
|
328
|
+
if (evaluateStyleRule(rule, feature, layerName)) {
|
|
329
|
+
return stylesMap[rule.styleId]
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
return undefined
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
export interface GsMap extends GsState {
|
|
337
|
+
view: GsView,
|
|
338
|
+
layers: GsLayer[],
|
|
339
|
+
overlays: GsOverlay[],
|
|
340
|
+
controls: GsControl[],
|
|
341
|
+
interactions: GsInteraction[],
|
|
342
|
+
styles: GsStylesMap,
|
|
343
|
+
styleRules: GsStyleRule[],
|
|
344
|
+
chatHistory: any[]
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
export const DEFAULT_STYLE_RULES: GsStyleRule[] = [
|
|
348
|
+
{
|
|
349
|
+
id: 'default-points',
|
|
350
|
+
condition: {
|
|
351
|
+
geometryType: [GsGeometryType.Point, GsGeometryType.MultiPoint]
|
|
352
|
+
},
|
|
353
|
+
styleId: 'default-point',
|
|
354
|
+
priority: 0
|
|
355
|
+
},
|
|
356
|
+
{
|
|
357
|
+
id: 'default-lines',
|
|
358
|
+
condition: {
|
|
359
|
+
geometryType: [GsGeometryType.LineString, GsGeometryType.MultiLineString]
|
|
360
|
+
},
|
|
361
|
+
styleId: 'default-line',
|
|
362
|
+
priority: 0
|
|
363
|
+
},
|
|
364
|
+
{
|
|
365
|
+
id: 'default-polygons',
|
|
366
|
+
condition: {
|
|
367
|
+
geometryType: [GsGeometryType.Polygon, GsGeometryType.MultiPolygon, GsGeometryType.Circle]
|
|
368
|
+
},
|
|
369
|
+
styleId: 'default-polygon',
|
|
370
|
+
priority: 0
|
|
371
|
+
}
|
|
372
|
+
]
|
|
373
|
+
|
|
374
|
+
export const DEFAULT_GSMAP = ensureUuid({
|
|
375
|
+
view: ensureUuid({
|
|
376
|
+
center: [0, 0],
|
|
377
|
+
zoom: 0,
|
|
378
|
+
projection: 'EPSG:3857'
|
|
379
|
+
} as GsView),
|
|
380
|
+
layers: [ensureUuid({
|
|
381
|
+
type: GsLayerType.TILE,
|
|
382
|
+
source: ensureUuid({
|
|
383
|
+
type: GsSourceType.OSM
|
|
384
|
+
} as GsSource),
|
|
385
|
+
} as GsLayer)],
|
|
386
|
+
overlays: [] as GsOverlay[],
|
|
387
|
+
controls: [] as GsControl[],
|
|
388
|
+
interactions: [] as GsInteraction[],
|
|
389
|
+
state: {},
|
|
390
|
+
styles: { ...DEFAULT_STYLES },
|
|
391
|
+
styleRules: [...DEFAULT_STYLE_RULES]
|
|
392
|
+
} as GsMap)
|
|
393
|
+
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import {Map} from "ol";
|
|
2
|
+
import {GsOlControl, GsOlOverlay} from "./gs-gs2ol";
|
|
3
|
+
import {render as litRender} from "@kispace-io/appspace/externals/lit-exports";
|
|
4
|
+
|
|
5
|
+
export class GsOlAdapter<T extends GsOlControl | GsOlOverlay> {
|
|
6
|
+
protected map: Map;
|
|
7
|
+
protected olObject: T;
|
|
8
|
+
|
|
9
|
+
private templateFunction?: Function;
|
|
10
|
+
protected retargetSelector?: string;
|
|
11
|
+
|
|
12
|
+
constructor(olObject: T) {
|
|
13
|
+
this.map = olObject.getMap()!
|
|
14
|
+
this.olObject = olObject
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
public getMap(): any {
|
|
18
|
+
return this.map;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
public getElement() {
|
|
22
|
+
return this.olObject.getElement();
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
public render(strings?: TemplateStringsArray | Function) {
|
|
26
|
+
if (strings === undefined && this.templateFunction) {
|
|
27
|
+
strings = this.templateFunction()
|
|
28
|
+
} else if (strings instanceof Function) {
|
|
29
|
+
this.templateFunction = strings as Function
|
|
30
|
+
strings = this.templateFunction()
|
|
31
|
+
}
|
|
32
|
+
if (strings) {
|
|
33
|
+
litRender(strings, this.getElement())
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
protected onRendered() {
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
public style(styleJson: any) {
|
|
41
|
+
const style = this.getElement().style
|
|
42
|
+
for (const property in styleJson) {
|
|
43
|
+
const value = styleJson[property]
|
|
44
|
+
style.setProperty(property, value);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export class GsControlAdapter extends GsOlAdapter<GsOlControl> {
|
|
50
|
+
public style(styleJson: any) {
|
|
51
|
+
super.style(styleJson)
|
|
52
|
+
|
|
53
|
+
if ("--gs-contribution" in styleJson) {
|
|
54
|
+
this.retargetSelector = styleJson["--gs-contribution"]
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
public rendered() {
|
|
59
|
+
if (this.retargetSelector) {
|
|
60
|
+
const [parentPath, queryString] = this.retargetSelector.split("?");
|
|
61
|
+
const parent = this.map.getTargetElement().querySelector(parentPath) as HTMLElement
|
|
62
|
+
if (parent) {
|
|
63
|
+
const element = this.olObject.getElement()
|
|
64
|
+
if (queryString) {
|
|
65
|
+
const params = new URLSearchParams(queryString);
|
|
66
|
+
const sibling = parent.querySelector(`[name='${params.get("before")}']`) as HTMLElement
|
|
67
|
+
if (sibling) {
|
|
68
|
+
parent.insertBefore(element, sibling)
|
|
69
|
+
}
|
|
70
|
+
} else {
|
|
71
|
+
parent.appendChild(element)
|
|
72
|
+
}
|
|
73
|
+
this.olObject.setTarget(element);
|
|
74
|
+
this.render()
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export class GsOverlayAdapter extends GsOlAdapter<GsOlOverlay> {
|
|
81
|
+
public show(coords: number[]) {
|
|
82
|
+
this.getElement().style.display = "block"
|
|
83
|
+
this.olObject.setPosition(coords)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
public hide() {
|
|
87
|
+
this.getElement().style.display = "none"
|
|
88
|
+
}
|
|
89
|
+
}
|