-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathviewer.class.ts
141 lines (135 loc) · 5.63 KB
/
viewer.class.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
import { Md5 } from 'ts-md5';
import {
Point,
Rect,
ViewAction,
ViewerFeature,
ViewerFocusFeature,
ViewerLabel,
ViewerOptions,
ViewerStyles,
} from './types';
/**
* @hidden
*/
export const EMPTY_BOX = { top: 0, left: 0, bottom: 0, right: 0, height: 0, width: 0 };
export interface Box {
readonly left: number;
readonly top: number;
readonly width: number;
readonly height: number;
}
export class Viewer {
/** Unique Identifier for the Viewer */
public id: string;
/** URL associated with the map data */
public url: string;
/** Element the SVG is attached */
public readonly element: HTMLElement | null;
/** Labels to render over the SVG */
public readonly labels: ViewerLabel[];
/** Features to render over the SVG */
public readonly features: ViewerFeature[];
/** Actions to listen for on the SVG */
public readonly actions: ViewAction[];
/** Point or Element to focus on in the viewer */
public readonly focus: ViewerFocusFeature | null;
/** Styles to apply the SVG */
public readonly styles: ViewerStyles;
/** Raw SVG data */
public readonly svg_data: string;
/** Zoom level of the SVG. Number from 1 - 10 */
public readonly zoom: number;
/** Center point of the SVG on the view */
public readonly center: Point;
/** Rotation angle of the SVG on the view */
public readonly rotate: number;
/** Ratio that the height to width of the SVG is */
public readonly ratio: number;
/** Ratio that the height to width of the SVG is */
public readonly svg_ratio: number;
/** Box dimensions for the root element of the viewer */
public readonly box: Box;
/** Zoom level of the SVG. Number from 1 - 10 */
public readonly desired_zoom: number;
/** Center point of the SVG on the view */
public readonly desired_center: Point;
/** Whether zoom and center still need updating */
public readonly needs_update: boolean;
/** */
public readonly options: ViewerOptions;
/** Max resolution for the render iframe image */
public readonly max_resolution: number;
/** */
public readonly updated_count: number;
/** Mapping of element positions to their IDs */
public readonly mappings: Record<string, Rect>;
/** Whether to use GPU for rendering map view changes */
public readonly use_gpu: boolean;
/** Ratio of the map rendered to the container element */
public readonly content_ratio: Point;
public contains(el_id: string) {
this.svg_data.includes(`id="${el_id}"`);
}
constructor(_data: Partial<Viewer>) {
this.id = _data.id || `map-${Math.floor(Math.random() * 999_999)}`;
this.url = _data.url || `local-${Md5.hashAsciiStr(_data.svg_data || '')}`;
this.element = _data.element || null;
this.labels = _data.labels || [];
this.features = _data.features || [];
this.actions = _data.actions || [];
this.styles = _data.styles || {};
this.svg_data = _data.svg_data || '';
this.content_ratio = _data.content_ratio || { x: 1, y: 1 };
this.zoom = _data.zoom || 1;
this.center = { x: _data.center?.x ?? 0.5, y: _data.center?.y ?? 0.5 };
this.rotate = _data.rotate || 0;
this.ratio = _data.ratio || 1;
this.svg_ratio = _data.svg_ratio || 1;
this.max_resolution = _data.max_resolution || window.innerWidth * window.innerHeight * 10;
this.focus = _data.focus || null;
this.options = _data.options || {};
this.mappings = _data.mappings || {};
this.box = {
top: (_data.box || EMPTY_BOX).top,
left: (_data.box || EMPTY_BOX).left,
height: (_data.box || EMPTY_BOX).height,
width: (_data.box || EMPTY_BOX).width,
};
this.desired_zoom = _data.desired_zoom || _data.zoom || this.zoom;
this.desired_center = {
x: _data.desired_center?.x || this.center.x,
y: _data.desired_center?.y || this.center.y,
};
this.updated_count = (_data.updated_count || 0) + 1;
if (this.zoom !== this.desired_zoom) {
const direction = this.desired_zoom - this.zoom >= 0 ? 1 : -1;
const change = Math.min(0.05, Math.abs(this.desired_zoom - this.zoom));
const ratio =
Math.round((change / Math.abs(this.desired_zoom - this.zoom)) * 1000) / 1000;
this.zoom = change === 0.05 ? this.zoom + direction * change : this.desired_zoom;
this.center = {
x: this.center.x + (this.desired_center.x - this.center.x) * ratio,
y: this.center.y + (this.desired_center.y - this.center.y) * ratio,
};
} else if (
this.desired_center.x !== this.center.x ||
this.desired_center.y !== this.center.y
) {
const x_direction = this.desired_center.x - this.center.x >= 0 ? 1 : -1;
const y_direction = this.desired_center.y - this.center.y >= 0 ? 1 : -1;
const x_change = Math.min(0.01, Math.abs(this.desired_center.x - this.center.x));
const ratio = x_change / Math.abs(this.desired_center.x - this.center.x);
const y_change = ratio * Math.abs(this.desired_center.y - this.center.y);
this.center = {
x: this.center.x + x_direction * x_change,
y: this.center.y + y_direction * y_change,
};
}
this.needs_update =
this.desired_zoom !== this.zoom ||
this.desired_center.x !== this.center.x ||
this.desired_center.y !== this.center.y;
this.use_gpu = _data?.use_gpu ?? true;
}
}