spagestic commited on
Commit
859b6ae
·
1 Parent(s): bcba258

globle files

Browse files
Files changed (4) hide show
  1. assets/globe.css +6 -0
  2. assets/globe.js +184 -0
  3. assets/globe_head.html +6 -0
  4. ui/globe.py +135 -0
assets/globe.css ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ /* assets/globe.css */
2
+ #map {
3
+ width: 100%;
4
+ height: 100%;
5
+ min-height: 100vh;
6
+ }
assets/globe.js ADDED
@@ -0,0 +1,184 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* assets/globe.js */
2
+ const GLOBE_DEFAULTS = {
3
+ style: "https://tiles.openfreemap.org/styles/liberty",
4
+ center: [0, 20],
5
+ zoom: 1.5,
6
+ minZoom: 0,
7
+ maxZoom: 22,
8
+ bearing: 0,
9
+ pitch: 0,
10
+ minPitch: 0,
11
+ maxPitch: 85,
12
+ projection: "globe",
13
+ attributionControl: true,
14
+ scrollZoom: true,
15
+ dragRotate: true,
16
+ dragPan: true,
17
+ keyboard: true,
18
+ doubleClickZoom: true,
19
+ touchZoomRotate: true,
20
+ interactive: true,
21
+ showGeolocateControl: true,
22
+ useCurrentLocation: false,
23
+ geolocateZoom: 10,
24
+ trackUserLocation: false,
25
+ showUserLocation: true,
26
+ showAccuracyCircle: true,
27
+ };
28
+
29
+ function parseBool(value, fallback) {
30
+ if (value === undefined) return fallback;
31
+ return value === "true" || value === "1";
32
+ }
33
+
34
+ function parseNumber(value, fallback) {
35
+ if (value === undefined || value === "") return fallback;
36
+ const n = Number(value);
37
+ return Number.isFinite(n) ? n : fallback;
38
+ }
39
+
40
+ function parseCenter(value, fallback) {
41
+ if (!value) return fallback;
42
+ try {
43
+ const parsed = JSON.parse(value);
44
+ if (Array.isArray(parsed) && parsed.length === 2) return parsed;
45
+ } catch {
46
+ const parts = value.split(",").map((s) => Number(s.trim()));
47
+ if (parts.length === 2 && parts.every(Number.isFinite)) return parts;
48
+ }
49
+ return fallback;
50
+ }
51
+
52
+ function readOptionsFromElement(el) {
53
+ const d = el.dataset;
54
+ return {
55
+ style: d.style,
56
+ center: parseCenter(d.center, undefined),
57
+ zoom: parseNumber(d.zoom, undefined),
58
+ minZoom: parseNumber(d.minZoom, undefined),
59
+ maxZoom: parseNumber(d.maxZoom, undefined),
60
+ bearing: parseNumber(d.bearing, undefined),
61
+ pitch: parseNumber(d.pitch, undefined),
62
+ minPitch: parseNumber(d.minPitch, undefined),
63
+ maxPitch: parseNumber(d.maxPitch, undefined),
64
+ projection: d.projection,
65
+ attributionControl: parseBool(d.attributionControl, undefined),
66
+ scrollZoom: parseBool(d.scrollZoom, undefined),
67
+ dragRotate: parseBool(d.dragRotate, undefined),
68
+ dragPan: parseBool(d.dragPan, undefined),
69
+ keyboard: parseBool(d.keyboard, undefined),
70
+ doubleClickZoom: parseBool(d.doubleClickZoom, undefined),
71
+ touchZoomRotate: parseBool(d.touchZoomRotate, undefined),
72
+ interactive: parseBool(d.interactive, undefined),
73
+ showGeolocateControl: parseBool(d.showGeolocateControl, undefined),
74
+ useCurrentLocation: parseBool(d.useCurrentLocation, undefined),
75
+ geolocateZoom: parseNumber(d.geolocateZoom, undefined),
76
+ trackUserLocation: parseBool(d.trackUserLocation, undefined),
77
+ showUserLocation: parseBool(d.showUserLocation, undefined),
78
+ showAccuracyCircle: parseBool(d.showAccuracyCircle, undefined),
79
+ };
80
+ }
81
+
82
+ function mergeGlobeOptions(...sources) {
83
+ const merged = { ...GLOBE_DEFAULTS };
84
+ for (const source of sources) {
85
+ if (!source) continue;
86
+ for (const [key, value] of Object.entries(source)) {
87
+ if (value !== undefined) merged[key] = value;
88
+ }
89
+ }
90
+ return merged;
91
+ }
92
+
93
+ function initGlobe(options = {}) {
94
+ if (typeof maplibregl === "undefined") {
95
+ setTimeout(() => initGlobe(options), 50);
96
+ return;
97
+ }
98
+
99
+ const container = element.querySelector("#map");
100
+ if (!container || container.dataset.mapInit) return;
101
+ container.dataset.mapInit = "1";
102
+
103
+ const opts = mergeGlobeOptions(readOptionsFromElement(container), options);
104
+ const {
105
+ projection,
106
+ attributionControl,
107
+ scrollZoom,
108
+ dragRotate,
109
+ dragPan,
110
+ keyboard,
111
+ doubleClickZoom,
112
+ touchZoomRotate,
113
+ interactive,
114
+ showGeolocateControl,
115
+ useCurrentLocation,
116
+ geolocateZoom,
117
+ trackUserLocation,
118
+ showUserLocation,
119
+ showAccuracyCircle,
120
+ ...mapOptions
121
+ } = opts;
122
+
123
+ const map = new maplibregl.Map({
124
+ container,
125
+ attributionControl,
126
+ scrollZoom,
127
+ dragRotate,
128
+ dragPan,
129
+ keyboard,
130
+ doubleClickZoom,
131
+ touchZoomRotate,
132
+ interactive,
133
+ ...mapOptions,
134
+ });
135
+
136
+ let geolocateControl = null;
137
+
138
+ if (showGeolocateControl) {
139
+ geolocateControl = new maplibregl.GeolocateControl({
140
+ positionOptions: { enableHighAccuracy: true },
141
+ trackUserLocation,
142
+ showUserLocation,
143
+ showAccuracyCircle,
144
+ });
145
+ map.addControl(geolocateControl, "top-right");
146
+ }
147
+
148
+ function flyToCurrentLocation() {
149
+ if (!navigator.geolocation) return;
150
+ navigator.geolocation.getCurrentPosition(
151
+ (pos) => {
152
+ map.flyTo({
153
+ center: [pos.coords.longitude, pos.coords.latitude],
154
+ zoom: geolocateZoom,
155
+ essential: true,
156
+ });
157
+ },
158
+ (err) => console.warn("Geolocation failed:", err.message),
159
+ { enableHighAccuracy: true }
160
+ );
161
+ }
162
+
163
+ map.on("style.load", () => {
164
+ map.setProjection({ type: projection });
165
+ map.resize();
166
+ });
167
+
168
+ map.on("load", () => {
169
+ map.resize();
170
+ if (useCurrentLocation) {
171
+ if (geolocateControl) {
172
+ geolocateControl.trigger();
173
+ } else {
174
+ flyToCurrentLocation();
175
+ }
176
+ }
177
+ });
178
+
179
+ window.addEventListener("resize", () => map.resize());
180
+
181
+ return map;
182
+ }
183
+
184
+ initGlobe();
assets/globe_head.html ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ <!-- assets/globe_head.html -->
2
+ <link
3
+ href="https://unpkg.com/maplibre-gl@5.24.0/dist/maplibre-gl.css"
4
+ rel="stylesheet"
5
+ />
6
+ <script src="https://unpkg.com/maplibre-gl@5.24.0/dist/maplibre-gl.js"></script>
ui/globe.py ADDED
@@ -0,0 +1,135 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # ui/globe.py
2
+ import json
3
+ from pathlib import Path
4
+
5
+ import gradio as gr
6
+
7
+ ASSETS_DIR = Path(__file__).resolve().parent.parent / "assets"
8
+
9
+
10
+ def _read_asset(name: str) -> str:
11
+ return (ASSETS_DIR / name).read_text(encoding="utf-8")
12
+
13
+
14
+ def _map_data_attrs(
15
+ *,
16
+ style: str = "https://tiles.openfreemap.org/styles/liberty",
17
+ center: tuple[float, float] = (0.0, 20.0),
18
+ zoom: float = 1.5,
19
+ min_zoom: float = 0.0,
20
+ max_zoom: float = 22.0,
21
+ bearing: float = 0.0,
22
+ pitch: float = 0.0,
23
+ min_pitch: float = 0.0,
24
+ max_pitch: float = 85.0,
25
+ projection: str = "globe",
26
+ attribution_control: bool = True,
27
+ scroll_zoom: bool = True,
28
+ drag_rotate: bool = True,
29
+ drag_pan: bool = True,
30
+ keyboard: bool = True,
31
+ double_click_zoom: bool = True,
32
+ touch_zoom_rotate: bool = True,
33
+ interactive: bool = True,
34
+ show_geolocate_control: bool = True,
35
+ use_current_location: bool = False,
36
+ geolocate_zoom: float = 10.0,
37
+ track_user_location: bool = False,
38
+ show_user_location: bool = True,
39
+ show_accuracy_circle: bool = True,
40
+ ) -> str:
41
+ attrs = {
42
+ "data-style": style,
43
+ "data-center": json.dumps(list(center)),
44
+ "data-zoom": zoom,
45
+ "data-min-zoom": min_zoom,
46
+ "data-max-zoom": max_zoom,
47
+ "data-bearing": bearing,
48
+ "data-pitch": pitch,
49
+ "data-min-pitch": min_pitch,
50
+ "data-max-pitch": max_pitch,
51
+ "data-projection": projection,
52
+ "data-attribution-control": str(attribution_control).lower(),
53
+ "data-scroll-zoom": str(scroll_zoom).lower(),
54
+ "data-drag-rotate": str(drag_rotate).lower(),
55
+ "data-drag-pan": str(drag_pan).lower(),
56
+ "data-keyboard": str(keyboard).lower(),
57
+ "data-double-click-zoom": str(double_click_zoom).lower(),
58
+ "data-touch-zoom-rotate": str(touch_zoom_rotate).lower(),
59
+ "data-interactive": str(interactive).lower(),
60
+ "data-show-geolocate-control": str(show_geolocate_control).lower(),
61
+ "data-use-current-location": str(use_current_location).lower(),
62
+ "data-geolocate-zoom": geolocate_zoom,
63
+ "data-track-user-location": str(track_user_location).lower(),
64
+ "data-show-user-location": str(show_user_location).lower(),
65
+ "data-show-accuracy-circle": str(show_accuracy_circle).lower(),
66
+ }
67
+ return " ".join(f'{key}="{value}"' for key, value in attrs.items())
68
+
69
+
70
+ def render_globe_panel(
71
+ *,
72
+ style: str = "https://tiles.openfreemap.org/styles/liberty",
73
+ center: tuple[float, float] = (0.0, 20.0),
74
+ zoom: float = 1.5,
75
+ min_zoom: float = 0.0,
76
+ max_zoom: float = 22.0,
77
+ bearing: float = 0.0,
78
+ pitch: float = 0.0,
79
+ min_pitch: float = 0.0,
80
+ max_pitch: float = 85.0,
81
+ projection: str = "globe",
82
+ attribution_control: bool = True,
83
+ scroll_zoom: bool = True,
84
+ drag_rotate: bool = True,
85
+ drag_pan: bool = True,
86
+ keyboard: bool = True,
87
+ double_click_zoom: bool = True,
88
+ touch_zoom_rotate: bool = True,
89
+ interactive: bool = True,
90
+ show_geolocate_control: bool = True,
91
+ use_current_location: bool = False,
92
+ geolocate_zoom: float = 10.0,
93
+ track_user_location: bool = False,
94
+ show_user_location: bool = True,
95
+ show_accuracy_circle: bool = True,
96
+ ):
97
+ data_attrs = _map_data_attrs(
98
+ style=style,
99
+ center=center,
100
+ zoom=zoom,
101
+ min_zoom=min_zoom,
102
+ max_zoom=max_zoom,
103
+ bearing=bearing,
104
+ pitch=pitch,
105
+ min_pitch=min_pitch,
106
+ max_pitch=max_pitch,
107
+ projection=projection,
108
+ attribution_control=attribution_control,
109
+ scroll_zoom=scroll_zoom,
110
+ drag_rotate=drag_rotate,
111
+ drag_pan=drag_pan,
112
+ keyboard=keyboard,
113
+ double_click_zoom=double_click_zoom,
114
+ touch_zoom_rotate=touch_zoom_rotate,
115
+ interactive=interactive,
116
+ show_geolocate_control=show_geolocate_control,
117
+ use_current_location=use_current_location,
118
+ geolocate_zoom=geolocate_zoom,
119
+ track_user_location=track_user_location,
120
+ show_user_location=show_user_location,
121
+ show_accuracy_circle=show_accuracy_circle,
122
+ )
123
+ return gr.HTML(
124
+ value=f'<div id="map" {data_attrs}></div>',
125
+ head=_read_asset("globe_head.html"),
126
+ css_template=_read_asset("globe.css"),
127
+ html_template="${value}",
128
+ js_on_load=_read_asset("globe.js"),
129
+ min_height="100vh",
130
+ show_label=False,
131
+ container=False,
132
+ padding=False,
133
+ scale=1,
134
+ elem_classes=["globe-panel"],
135
+ )