Upload folder using huggingface_hub
Browse files- .github/workflows/static.yml +43 -0
- L.KML.js +482 -0
- README.md +1 -12
- data/Bhankhara-Df-11-he-5-2020-21.kml +84 -0
- data/Bhankhara_Df_11_he_5_2020-21.geojson +8 -0
- geojson.html +94 -0
- geostats.js +52 -0
- kml.html +55 -0
- streamlit-app.py +93 -0
- style.css +86 -0
.github/workflows/static.yml
ADDED
@@ -0,0 +1,43 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Simple workflow for deploying static content to GitHub Pages
|
2 |
+
name: Deploy static content to Pages
|
3 |
+
|
4 |
+
on:
|
5 |
+
# Runs on pushes targeting the default branch
|
6 |
+
push:
|
7 |
+
branches: ["main"]
|
8 |
+
|
9 |
+
# Allows you to run this workflow manually from the Actions tab
|
10 |
+
workflow_dispatch:
|
11 |
+
|
12 |
+
# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
|
13 |
+
permissions:
|
14 |
+
contents: read
|
15 |
+
pages: write
|
16 |
+
id-token: write
|
17 |
+
|
18 |
+
# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.
|
19 |
+
# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete.
|
20 |
+
concurrency:
|
21 |
+
group: "pages"
|
22 |
+
cancel-in-progress: false
|
23 |
+
|
24 |
+
jobs:
|
25 |
+
# Single deploy job since we're just deploying
|
26 |
+
deploy:
|
27 |
+
environment:
|
28 |
+
name: github-pages
|
29 |
+
url: ${{ steps.deployment.outputs.page_url }}
|
30 |
+
runs-on: ubuntu-latest
|
31 |
+
steps:
|
32 |
+
- name: Checkout
|
33 |
+
uses: actions/checkout@v4
|
34 |
+
- name: Setup Pages
|
35 |
+
uses: actions/configure-pages@v5
|
36 |
+
- name: Upload artifact
|
37 |
+
uses: actions/upload-pages-artifact@v3
|
38 |
+
with:
|
39 |
+
# Upload entire repository
|
40 |
+
path: '.'
|
41 |
+
- name: Deploy to GitHub Pages
|
42 |
+
id: deployment
|
43 |
+
uses: actions/deploy-pages@v4
|
L.KML.js
ADDED
@@ -0,0 +1,482 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/*!
|
2 |
+
Copyright (c) 2011-2015, Pavel Shramov, Bruno Bergot - MIT licence
|
3 |
+
*/
|
4 |
+
|
5 |
+
L.KML = L.FeatureGroup.extend({
|
6 |
+
|
7 |
+
initialize: function (kml, kmlOptions) {
|
8 |
+
this._kml = kml;
|
9 |
+
this._layers = {};
|
10 |
+
this._kmlOptions = kmlOptions;
|
11 |
+
|
12 |
+
if (kml) {
|
13 |
+
this.addKML(kml, kmlOptions);
|
14 |
+
}
|
15 |
+
},
|
16 |
+
|
17 |
+
addKML: function (xml, kmlOptions) {
|
18 |
+
var layers = L.KML.parseKML(xml, kmlOptions);
|
19 |
+
if (!layers || !layers.length) return;
|
20 |
+
for (var i = 0; i < layers.length; i++) {
|
21 |
+
this.fire('addlayer', {
|
22 |
+
layer: layers[i]
|
23 |
+
});
|
24 |
+
this.addLayer(layers[i]);
|
25 |
+
}
|
26 |
+
this.latLngs = L.KML.getLatLngs(xml);
|
27 |
+
this.fire('loaded');
|
28 |
+
},
|
29 |
+
|
30 |
+
latLngs: []
|
31 |
+
});
|
32 |
+
|
33 |
+
L.Util.extend(L.KML, {
|
34 |
+
|
35 |
+
parseKML: function (xml, kmlOptions) {
|
36 |
+
var style = this.parseStyles(xml, kmlOptions);
|
37 |
+
this.parseStyleMap(xml, style);
|
38 |
+
var el = xml.getElementsByTagName('Folder');
|
39 |
+
var layers = [], l;
|
40 |
+
for (var i = 0; i < el.length; i++) {
|
41 |
+
if (!this._check_folder(el[i])) { continue; }
|
42 |
+
l = this.parseFolder(el[i], style);
|
43 |
+
if (l) { layers.push(l); }
|
44 |
+
}
|
45 |
+
el = xml.getElementsByTagName('Placemark');
|
46 |
+
for (var j = 0; j < el.length; j++) {
|
47 |
+
if (!this._check_folder(el[j])) { continue; }
|
48 |
+
l = this.parsePlacemark(el[j], xml, style);
|
49 |
+
if (l) { layers.push(l); }
|
50 |
+
}
|
51 |
+
el = xml.getElementsByTagName('GroundOverlay');
|
52 |
+
for (var k = 0; k < el.length; k++) {
|
53 |
+
l = this.parseGroundOverlay(el[k]);
|
54 |
+
if (l) { layers.push(l); }
|
55 |
+
}
|
56 |
+
return layers;
|
57 |
+
},
|
58 |
+
|
59 |
+
// Return false if e's first parent Folder is not [folder]
|
60 |
+
// - returns true if no parent Folders
|
61 |
+
_check_folder: function (e, folder) {
|
62 |
+
e = e.parentNode;
|
63 |
+
while (e && e.tagName !== 'Folder')
|
64 |
+
{
|
65 |
+
e = e.parentNode;
|
66 |
+
}
|
67 |
+
return !e || e === folder;
|
68 |
+
},
|
69 |
+
|
70 |
+
parseStyles: function (xml, kmlOptions) {
|
71 |
+
var styles = {};
|
72 |
+
var sl = xml.getElementsByTagName('Style');
|
73 |
+
for (var i=0, len=sl.length; i<len; i++) {
|
74 |
+
var style = this.parseStyle(sl[i], kmlOptions);
|
75 |
+
if (style) {
|
76 |
+
var styleName = '#' + style.id;
|
77 |
+
styles[styleName] = style;
|
78 |
+
}
|
79 |
+
}
|
80 |
+
return styles;
|
81 |
+
},
|
82 |
+
|
83 |
+
parseStyle: function (xml, kmlOptions) {
|
84 |
+
var style = {}, poptions = {}, ioptions = {}, el, id;
|
85 |
+
|
86 |
+
var attributes = {color: true, width: true, Icon: true, href: true, hotSpot: true};
|
87 |
+
|
88 |
+
function _parse (xml) {
|
89 |
+
var options = {};
|
90 |
+
for (var i = 0; i < xml.childNodes.length; i++) {
|
91 |
+
var e = xml.childNodes[i];
|
92 |
+
var key = e.tagName;
|
93 |
+
if (!attributes[key]) { continue; }
|
94 |
+
if (key === 'hotSpot')
|
95 |
+
{
|
96 |
+
for (var j = 0; j < e.attributes.length; j++) {
|
97 |
+
options[e.attributes[j].name] = e.attributes[j].nodeValue;
|
98 |
+
}
|
99 |
+
} else {
|
100 |
+
var value = (e.childNodes && e.childNodes.length) ? e.childNodes[0].nodeValue : null;
|
101 |
+
if(!value) {
|
102 |
+
continue;
|
103 |
+
}
|
104 |
+
if (key === 'color') {
|
105 |
+
options.opacity = parseInt(value.substring(0, 2), 16) / 255.0;
|
106 |
+
options.color = '#' + value.substring(6, 8) + value.substring(4, 6) + value.substring(2, 4);
|
107 |
+
} else if (key === 'width') {
|
108 |
+
options.weight = parseInt(value);
|
109 |
+
} else if (key === 'Icon') {
|
110 |
+
ioptions = _parse(e);
|
111 |
+
if (ioptions.href) { options.href = ioptions.href; }
|
112 |
+
} else if (key === 'href') {
|
113 |
+
options.href = value;
|
114 |
+
}
|
115 |
+
}
|
116 |
+
}
|
117 |
+
return options;
|
118 |
+
}
|
119 |
+
|
120 |
+
el = xml.getElementsByTagName('LineStyle');
|
121 |
+
if (el && el[0]) { style = _parse(el[0]); }
|
122 |
+
el = xml.getElementsByTagName('PolyStyle');
|
123 |
+
if (el && el[0]) { poptions = _parse(el[0]); }
|
124 |
+
if (poptions.color) { style.fillColor = poptions.color; }
|
125 |
+
if (poptions.opacity) { style.fillOpacity = poptions.opacity; }
|
126 |
+
el = xml.getElementsByTagName('IconStyle');
|
127 |
+
if (el && el[0]) { ioptions = _parse(el[0]); }
|
128 |
+
if (ioptions.href) {
|
129 |
+
var iconOptions = {
|
130 |
+
iconUrl: ioptions.href,
|
131 |
+
shadowUrl: null,
|
132 |
+
anchorRef: {x: ioptions.x, y: ioptions.y},
|
133 |
+
anchorType: {x: ioptions.xunits, y: ioptions.yunits}
|
134 |
+
};
|
135 |
+
|
136 |
+
if (typeof kmlOptions === "object" && typeof kmlOptions.iconOptions === "object") {
|
137 |
+
L.Util.extend(iconOptions, kmlOptions.iconOptions);
|
138 |
+
}
|
139 |
+
|
140 |
+
style.icon = new L.KMLIcon(iconOptions);
|
141 |
+
}
|
142 |
+
|
143 |
+
id = xml.getAttribute('id');
|
144 |
+
if (id && style) {
|
145 |
+
style.id = id;
|
146 |
+
}
|
147 |
+
|
148 |
+
return style;
|
149 |
+
},
|
150 |
+
|
151 |
+
parseStyleMap: function (xml, existingStyles) {
|
152 |
+
var sl = xml.getElementsByTagName('StyleMap');
|
153 |
+
|
154 |
+
for (var i = 0; i < sl.length; i++) {
|
155 |
+
var e = sl[i], el;
|
156 |
+
var smKey, smStyleUrl;
|
157 |
+
|
158 |
+
el = e.getElementsByTagName('key');
|
159 |
+
if (el && el[0]) { smKey = el[0].textContent; }
|
160 |
+
el = e.getElementsByTagName('styleUrl');
|
161 |
+
if (el && el[0]) { smStyleUrl = el[0].textContent; }
|
162 |
+
|
163 |
+
if (smKey === 'normal')
|
164 |
+
{
|
165 |
+
existingStyles['#' + e.getAttribute('id')] = existingStyles[smStyleUrl];
|
166 |
+
}
|
167 |
+
}
|
168 |
+
|
169 |
+
return;
|
170 |
+
},
|
171 |
+
|
172 |
+
parseFolder: function (xml, style) {
|
173 |
+
var el, layers = [], l;
|
174 |
+
el = xml.getElementsByTagName('Folder');
|
175 |
+
for (var i = 0; i < el.length; i++) {
|
176 |
+
if (!this._check_folder(el[i], xml)) { continue; }
|
177 |
+
l = this.parseFolder(el[i], style);
|
178 |
+
if (l) { layers.push(l); }
|
179 |
+
}
|
180 |
+
el = xml.getElementsByTagName('Placemark');
|
181 |
+
for (var j = 0; j < el.length; j++) {
|
182 |
+
if (!this._check_folder(el[j], xml)) { continue; }
|
183 |
+
l = this.parsePlacemark(el[j], xml, style);
|
184 |
+
if (l) { layers.push(l); }
|
185 |
+
}
|
186 |
+
el = xml.getElementsByTagName('GroundOverlay');
|
187 |
+
for (var k = 0; k < el.length; k++) {
|
188 |
+
if (!this._check_folder(el[k], xml)) { continue; }
|
189 |
+
l = this.parseGroundOverlay(el[k]);
|
190 |
+
if (l) { layers.push(l); }
|
191 |
+
}
|
192 |
+
if (!layers.length) { return; }
|
193 |
+
if (layers.length === 1) {
|
194 |
+
l = layers[0];
|
195 |
+
} else {
|
196 |
+
l = new L.FeatureGroup(layers);
|
197 |
+
}
|
198 |
+
el = xml.getElementsByTagName('name');
|
199 |
+
if (el.length && el[0].childNodes.length) {
|
200 |
+
l.options.name = el[0].childNodes[0].nodeValue;
|
201 |
+
}
|
202 |
+
return l;
|
203 |
+
},
|
204 |
+
|
205 |
+
parsePlacemark: function (place, xml, style, options) {
|
206 |
+
var h, i, j, k, el, il, opts = options || {};
|
207 |
+
|
208 |
+
el = place.getElementsByTagName('styleUrl');
|
209 |
+
for (i = 0; i < el.length; i++) {
|
210 |
+
var url = el[i].childNodes[0].nodeValue;
|
211 |
+
for (var a in style[url]) {
|
212 |
+
opts[a] = style[url][a];
|
213 |
+
}
|
214 |
+
}
|
215 |
+
|
216 |
+
il = place.getElementsByTagName('Style')[0];
|
217 |
+
if (il) {
|
218 |
+
var inlineStyle = this.parseStyle(place);
|
219 |
+
if (inlineStyle) {
|
220 |
+
for (k in inlineStyle) {
|
221 |
+
opts[k] = inlineStyle[k];
|
222 |
+
}
|
223 |
+
}
|
224 |
+
}
|
225 |
+
|
226 |
+
var multi = ['MultiGeometry', 'MultiTrack', 'gx:MultiTrack'];
|
227 |
+
for (h in multi) {
|
228 |
+
el = place.getElementsByTagName(multi[h]);
|
229 |
+
for (i = 0; i < el.length; i++) {
|
230 |
+
var layer = this.parsePlacemark(el[i], xml, style, opts);
|
231 |
+
if (layer === undefined)
|
232 |
+
continue;
|
233 |
+
this.addPlacePopup(place, layer);
|
234 |
+
return layer;
|
235 |
+
}
|
236 |
+
}
|
237 |
+
|
238 |
+
var layers = [];
|
239 |
+
|
240 |
+
var parse = ['LineString', 'Polygon', 'Point', 'Track', 'gx:Track'];
|
241 |
+
for (j in parse) {
|
242 |
+
var tag = parse[j];
|
243 |
+
el = place.getElementsByTagName(tag);
|
244 |
+
for (i = 0; i < el.length; i++) {
|
245 |
+
var l = this['parse' + tag.replace(/gx:/, '')](el[i], xml, opts);
|
246 |
+
if (l) { layers.push(l); }
|
247 |
+
}
|
248 |
+
}
|
249 |
+
|
250 |
+
if (!layers.length) {
|
251 |
+
return;
|
252 |
+
}
|
253 |
+
var layer = layers[0];
|
254 |
+
if (layers.length > 1) {
|
255 |
+
layer = new L.FeatureGroup(layers);
|
256 |
+
}
|
257 |
+
|
258 |
+
this.addPlacePopup(place, layer);
|
259 |
+
return layer;
|
260 |
+
},
|
261 |
+
|
262 |
+
addPlacePopup: function(place, layer) {
|
263 |
+
var el, i, j, name, descr = '';
|
264 |
+
el = place.getElementsByTagName('name');
|
265 |
+
if (el.length && el[0].childNodes.length) {
|
266 |
+
name = el[0].childNodes[0].nodeValue;
|
267 |
+
}
|
268 |
+
el = place.getElementsByTagName('description');
|
269 |
+
for (i = 0; i < el.length; i++) {
|
270 |
+
for (j = 0; j < el[i].childNodes.length; j++) {
|
271 |
+
descr = descr + el[i].childNodes[j].nodeValue;
|
272 |
+
}
|
273 |
+
}
|
274 |
+
|
275 |
+
if (name) {
|
276 |
+
layer.bindPopup('<h2>' + name + '</h2>' + descr, { className: 'kml-popup'});
|
277 |
+
}
|
278 |
+
},
|
279 |
+
|
280 |
+
parseCoords: function (xml) {
|
281 |
+
var el = xml.getElementsByTagName('coordinates');
|
282 |
+
return this._read_coords(el[0]);
|
283 |
+
},
|
284 |
+
|
285 |
+
parseLineString: function (line, xml, options) {
|
286 |
+
var coords = this.parseCoords(line);
|
287 |
+
if (!coords.length) { return; }
|
288 |
+
return new L.Polyline(coords, options);
|
289 |
+
},
|
290 |
+
|
291 |
+
parseTrack: function (line, xml, options) {
|
292 |
+
var el = xml.getElementsByTagName('gx:coord');
|
293 |
+
if (el.length === 0) { el = xml.getElementsByTagName('coord'); }
|
294 |
+
var coords = [];
|
295 |
+
for (var j = 0; j < el.length; j++) {
|
296 |
+
coords = coords.concat(this._read_gxcoords(el[j]));
|
297 |
+
}
|
298 |
+
if (!coords.length) { return; }
|
299 |
+
return new L.Polyline(coords, options);
|
300 |
+
},
|
301 |
+
|
302 |
+
parsePoint: function (line, xml, options) {
|
303 |
+
var el = line.getElementsByTagName('coordinates');
|
304 |
+
if (!el.length) {
|
305 |
+
return;
|
306 |
+
}
|
307 |
+
var ll = el[0].childNodes[0].nodeValue.split(',');
|
308 |
+
return new L.KMLMarker(new L.LatLng(ll[1], ll[0]), options);
|
309 |
+
},
|
310 |
+
|
311 |
+
parsePolygon: function (line, xml, options) {
|
312 |
+
var el, polys = [], inner = [], i, coords;
|
313 |
+
el = line.getElementsByTagName('outerBoundaryIs');
|
314 |
+
for (i = 0; i < el.length; i++) {
|
315 |
+
coords = this.parseCoords(el[i]);
|
316 |
+
if (coords) {
|
317 |
+
polys.push(coords);
|
318 |
+
}
|
319 |
+
}
|
320 |
+
el = line.getElementsByTagName('innerBoundaryIs');
|
321 |
+
for (i = 0; i < el.length; i++) {
|
322 |
+
coords = this.parseCoords(el[i]);
|
323 |
+
if (coords) {
|
324 |
+
inner.push(coords);
|
325 |
+
}
|
326 |
+
}
|
327 |
+
if (!polys.length) {
|
328 |
+
return;
|
329 |
+
}
|
330 |
+
if (options.fillColor) {
|
331 |
+
options.fill = true;
|
332 |
+
}
|
333 |
+
if (polys.length === 1) {
|
334 |
+
return new L.Polygon(polys.concat(inner), options);
|
335 |
+
}
|
336 |
+
return new L.MultiPolygon(polys, options);
|
337 |
+
},
|
338 |
+
|
339 |
+
getLatLngs: function (xml) {
|
340 |
+
var el = xml.getElementsByTagName('coordinates');
|
341 |
+
var coords = [];
|
342 |
+
for (var j = 0; j < el.length; j++) {
|
343 |
+
// text might span many childNodes
|
344 |
+
coords = coords.concat(this._read_coords(el[j]));
|
345 |
+
}
|
346 |
+
return coords;
|
347 |
+
},
|
348 |
+
|
349 |
+
_read_coords: function (el) {
|
350 |
+
var text = '', coords = [], i;
|
351 |
+
for (i = 0; i < el.childNodes.length; i++) {
|
352 |
+
text = text + el.childNodes[i].nodeValue;
|
353 |
+
}
|
354 |
+
text = text.split(/[\s\n]+/);
|
355 |
+
for (i = 0; i < text.length; i++) {
|
356 |
+
var ll = text[i].split(',');
|
357 |
+
if (ll.length < 2) {
|
358 |
+
continue;
|
359 |
+
}
|
360 |
+
coords.push(new L.LatLng(ll[1], ll[0]));
|
361 |
+
}
|
362 |
+
return coords;
|
363 |
+
},
|
364 |
+
|
365 |
+
_read_gxcoords: function (el) {
|
366 |
+
var text = '', coords = [];
|
367 |
+
text = el.firstChild.nodeValue.split(' ');
|
368 |
+
coords.push(new L.LatLng(text[1], text[0]));
|
369 |
+
return coords;
|
370 |
+
},
|
371 |
+
|
372 |
+
parseGroundOverlay: function (xml) {
|
373 |
+
var latlonbox = xml.getElementsByTagName('LatLonBox')[0];
|
374 |
+
var bounds = new L.LatLngBounds(
|
375 |
+
[
|
376 |
+
latlonbox.getElementsByTagName('south')[0].childNodes[0].nodeValue,
|
377 |
+
latlonbox.getElementsByTagName('west')[0].childNodes[0].nodeValue
|
378 |
+
],
|
379 |
+
[
|
380 |
+
latlonbox.getElementsByTagName('north')[0].childNodes[0].nodeValue,
|
381 |
+
latlonbox.getElementsByTagName('east')[0].childNodes[0].nodeValue
|
382 |
+
]
|
383 |
+
);
|
384 |
+
var attributes = {Icon: true, href: true, color: true};
|
385 |
+
function _parse (xml) {
|
386 |
+
var options = {}, ioptions = {};
|
387 |
+
for (var i = 0; i < xml.childNodes.length; i++) {
|
388 |
+
var e = xml.childNodes[i];
|
389 |
+
var key = e.tagName;
|
390 |
+
if (!attributes[key]) { continue; }
|
391 |
+
var value = e.childNodes[0].nodeValue;
|
392 |
+
if (key === 'Icon') {
|
393 |
+
ioptions = _parse(e);
|
394 |
+
if (ioptions.href) { options.href = ioptions.href; }
|
395 |
+
} else if (key === 'href') {
|
396 |
+
options.href = value;
|
397 |
+
} else if (key === 'color') {
|
398 |
+
options.opacity = parseInt(value.substring(0, 2), 16) / 255.0;
|
399 |
+
options.color = '#' + value.substring(6, 8) + value.substring(4, 6) + value.substring(2, 4);
|
400 |
+
}
|
401 |
+
}
|
402 |
+
return options;
|
403 |
+
}
|
404 |
+
var options = {};
|
405 |
+
options = _parse(xml);
|
406 |
+
if (latlonbox.getElementsByTagName('rotation')[0] !== undefined) {
|
407 |
+
var rotation = latlonbox.getElementsByTagName('rotation')[0].childNodes[0].nodeValue;
|
408 |
+
options.rotation = parseFloat(rotation);
|
409 |
+
}
|
410 |
+
return new L.RotatedImageOverlay(options.href, bounds, {opacity: options.opacity, angle: options.rotation});
|
411 |
+
}
|
412 |
+
|
413 |
+
});
|
414 |
+
|
415 |
+
L.KMLIcon = L.Icon.extend({
|
416 |
+
options: {
|
417 |
+
iconSize: [32, 32],
|
418 |
+
iconAnchor: [16, 16],
|
419 |
+
},
|
420 |
+
_setIconStyles: function (img, name) {
|
421 |
+
L.Icon.prototype._setIconStyles.apply(this, [img, name]);
|
422 |
+
},
|
423 |
+
_createImg: function (src, el) {
|
424 |
+
el = el || document.createElement('img');
|
425 |
+
el.onload = this.applyCustomStyles.bind(this,el)
|
426 |
+
el.src = src;
|
427 |
+
return el;
|
428 |
+
},
|
429 |
+
applyCustomStyles: function(img) {
|
430 |
+
var options = this.options;
|
431 |
+
var width = options.iconSize[0];
|
432 |
+
var height = options.iconSize[1];
|
433 |
+
|
434 |
+
this.options.popupAnchor = [0,(-0.83*height)];
|
435 |
+
if (options.anchorType.x === 'fraction')
|
436 |
+
img.style.marginLeft = (-options.anchorRef.x * width) + 'px';
|
437 |
+
if (options.anchorType.y === 'fraction')
|
438 |
+
img.style.marginTop = ((-(1 - options.anchorRef.y) * height) + 1) + 'px';
|
439 |
+
if (options.anchorType.x === 'pixels')
|
440 |
+
img.style.marginLeft = (-options.anchorRef.x) + 'px';
|
441 |
+
if (options.anchorType.y === 'pixels')
|
442 |
+
img.style.marginTop = (options.anchorRef.y - height + 1) + 'px';
|
443 |
+
}
|
444 |
+
});
|
445 |
+
|
446 |
+
|
447 |
+
L.KMLMarker = L.Marker.extend({
|
448 |
+
options: {
|
449 |
+
icon: new L.KMLIcon.Default()
|
450 |
+
}
|
451 |
+
});
|
452 |
+
|
453 |
+
// Inspired by https://github.com/bbecquet/Leaflet.PolylineDecorator/tree/master/src
|
454 |
+
L.RotatedImageOverlay = L.ImageOverlay.extend({
|
455 |
+
options: {
|
456 |
+
angle: 0
|
457 |
+
},
|
458 |
+
_reset: function () {
|
459 |
+
L.ImageOverlay.prototype._reset.call(this);
|
460 |
+
this._rotate();
|
461 |
+
},
|
462 |
+
_animateZoom: function (e) {
|
463 |
+
L.ImageOverlay.prototype._animateZoom.call(this, e);
|
464 |
+
this._rotate();
|
465 |
+
},
|
466 |
+
_rotate: function () {
|
467 |
+
if (L.DomUtil.TRANSFORM) {
|
468 |
+
// use the CSS transform rule if available
|
469 |
+
this._image.style[L.DomUtil.TRANSFORM] += ' rotate(' + this.options.angle + 'deg)';
|
470 |
+
} else if (L.Browser.ie) {
|
471 |
+
// fallback for IE6, IE7, IE8
|
472 |
+
var rad = this.options.angle * (Math.PI / 180),
|
473 |
+
costheta = Math.cos(rad),
|
474 |
+
sintheta = Math.sin(rad);
|
475 |
+
this._image.style.filter += ' progid:DXImageTransform.Microsoft.Matrix(sizingMethod=\'auto expand\', M11=' +
|
476 |
+
costheta + ', M12=' + (-sintheta) + ', M21=' + sintheta + ', M22=' + costheta + ')';
|
477 |
+
}
|
478 |
+
},
|
479 |
+
getBounds: function () {
|
480 |
+
return this._bounds;
|
481 |
+
}
|
482 |
+
});
|
README.md
CHANGED
@@ -1,12 +1 @@
|
|
1 |
-
|
2 |
-
title: Bhuvan
|
3 |
-
emoji: 🐨
|
4 |
-
colorFrom: yellow
|
5 |
-
colorTo: blue
|
6 |
-
sdk: gradio
|
7 |
-
sdk_version: 5.4.0
|
8 |
-
app_file: app.py
|
9 |
-
pinned: false
|
10 |
-
---
|
11 |
-
|
12 |
-
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
|
|
1 |
+
# bhuvan
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
data/Bhankhara-Df-11-he-5-2020-21.kml
ADDED
@@ -0,0 +1,84 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?xml version="1.0" encoding="UTF-8"?>
|
2 |
+
<kml xmlns="http://www.opengis.net/kml/2.2" xmlns:gx="http://www.google.com/kml/ext/2.2" xmlns:kml="http://www.opengis.net/kml/2.2" xmlns:atom="http://www.w3.org/2005/Atom">
|
3 |
+
<Document>
|
4 |
+
<name>Bhankhara Df 11 he 5 2020-21</name>
|
5 |
+
<gx:CascadingStyle kml:id="__managed_style_2ECD90080C332BDE40E2">
|
6 |
+
<Style>
|
7 |
+
<IconStyle>
|
8 |
+
<scale>1.2</scale>
|
9 |
+
<Icon>
|
10 |
+
<href>https://earth.google.com/earth/rpc/cc/icon?color=1976d2&id=2000&scale=4</href>
|
11 |
+
</Icon>
|
12 |
+
<hotSpot x="64" y="128" xunits="pixels" yunits="insetPixels"/>
|
13 |
+
</IconStyle>
|
14 |
+
<LabelStyle>
|
15 |
+
</LabelStyle>
|
16 |
+
<LineStyle>
|
17 |
+
<color>ff2dc0fb</color>
|
18 |
+
<width>2.4389</width>
|
19 |
+
</LineStyle>
|
20 |
+
<PolyStyle>
|
21 |
+
<color>00ffffff</color>
|
22 |
+
</PolyStyle>
|
23 |
+
<BalloonStyle>
|
24 |
+
<displayMode>hide</displayMode>
|
25 |
+
</BalloonStyle>
|
26 |
+
</Style>
|
27 |
+
</gx:CascadingStyle>
|
28 |
+
<gx:CascadingStyle kml:id="__managed_style_175A38A9FB332BDE40E2">
|
29 |
+
<Style>
|
30 |
+
<IconStyle>
|
31 |
+
<Icon>
|
32 |
+
<href>https://earth.google.com/earth/rpc/cc/icon?color=1976d2&id=2000&scale=4</href>
|
33 |
+
</Icon>
|
34 |
+
<hotSpot x="64" y="128" xunits="pixels" yunits="insetPixels"/>
|
35 |
+
</IconStyle>
|
36 |
+
<LabelStyle>
|
37 |
+
</LabelStyle>
|
38 |
+
<LineStyle>
|
39 |
+
<color>ff2dc0fb</color>
|
40 |
+
<width>1.62594</width>
|
41 |
+
</LineStyle>
|
42 |
+
<PolyStyle>
|
43 |
+
<color>00ffffff</color>
|
44 |
+
</PolyStyle>
|
45 |
+
<BalloonStyle>
|
46 |
+
<displayMode>hide</displayMode>
|
47 |
+
</BalloonStyle>
|
48 |
+
</Style>
|
49 |
+
</gx:CascadingStyle>
|
50 |
+
<StyleMap id="__managed_style_02B2DF3F10332BDE40E2">
|
51 |
+
<Pair>
|
52 |
+
<key>normal</key>
|
53 |
+
<styleUrl>#__managed_style_175A38A9FB332BDE40E2</styleUrl>
|
54 |
+
</Pair>
|
55 |
+
<Pair>
|
56 |
+
<key>highlight</key>
|
57 |
+
<styleUrl>#__managed_style_2ECD90080C332BDE40E2</styleUrl>
|
58 |
+
</Pair>
|
59 |
+
</StyleMap>
|
60 |
+
<Placemark id="0CB71842DC32C34FCCDC">
|
61 |
+
<name>bhankhara Df </name>
|
62 |
+
<LookAt>
|
63 |
+
<longitude>73.23288329025303</longitude>
|
64 |
+
<latitude>23.92934021215672</latitude>
|
65 |
+
<altitude>351.5436929053654</altitude>
|
66 |
+
<heading>22.18919554855782</heading>
|
67 |
+
<tilt>0</tilt>
|
68 |
+
<gx:fovy>30.00004101</gx:fovy>
|
69 |
+
<range>1627.012775921321</range>
|
70 |
+
<altitudeMode>absolute</altitudeMode>
|
71 |
+
</LookAt>
|
72 |
+
<styleUrl>#__managed_style_02B2DF3F10332BDE40E2</styleUrl>
|
73 |
+
<Polygon>
|
74 |
+
<outerBoundaryIs>
|
75 |
+
<LinearRing>
|
76 |
+
<coordinates>
|
77 |
+
73.23288020595453,23.92934920152165,0 73.2320424558106,23.9295414782706,0 73.23177030588151,23.9290796121952,0 73.23049283479376,23.92975711242018,0 73.2296710069635,23.93046123326063,0 73.2287325848189,23.92942606846368,0 73.23001098215263,23.92845402165482,0 73.23132082595696,23.92741616938989,0 73.23174863262777,23.92774125965506,0 73.23240516734252,23.92789815610602,0 73.23288020595453,23.92934920152165,0
|
78 |
+
</coordinates>
|
79 |
+
</LinearRing>
|
80 |
+
</outerBoundaryIs>
|
81 |
+
</Polygon>
|
82 |
+
</Placemark>
|
83 |
+
</Document>
|
84 |
+
</kml>
|
data/Bhankhara_Df_11_he_5_2020-21.geojson
ADDED
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"type": "FeatureCollection",
|
3 |
+
"name": "Bhankhara Df 11 he 5 2020-21",
|
4 |
+
"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:OGC:1.3:CRS84" } },
|
5 |
+
"features": [
|
6 |
+
{ "type": "Feature", "properties": { "Name": "bhankhara Df " }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 73.232880205954501, 23.9293492015216 ], [ 73.2320424558106, 23.9295414782706 ], [ 73.231770305881497, 23.929079612195199 ], [ 73.230492834793793, 23.929757112420202 ], [ 73.2296710069635, 23.930461233260601 ], [ 73.2287325848189, 23.929426068463702 ], [ 73.230010982152606, 23.928454021654801 ], [ 73.231320825956999, 23.9274161693899 ], [ 73.231748632627799, 23.9277412596551 ], [ 73.232405167342506, 23.927898156106 ], [ 73.232880205954501, 23.9293492015216 ] ] ] } }
|
7 |
+
]
|
8 |
+
}
|
geojson.html
ADDED
@@ -0,0 +1,94 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<html>
|
2 |
+
<head>
|
3 |
+
<link rel="stylesheet" href="https://unpkg.com/[email protected]/dist/leaflet.css" />
|
4 |
+
<script src="https://unpkg.com/[email protected]/dist/leaflet.js"></script>
|
5 |
+
<script src="https://unpkg.com/@turf/[email protected]/turf.min.js"></script>
|
6 |
+
<link rel="stylesheet" href="style.css"> <!-- Link to the external style.css file -->
|
7 |
+
|
8 |
+
</head>
|
9 |
+
|
10 |
+
<body>
|
11 |
+
<header>Interactive GeoJSON Map Viewer</header>
|
12 |
+
|
13 |
+
<div id="map"></div>
|
14 |
+
|
15 |
+
<div id="stats">
|
16 |
+
<h3>GeoJSON Statistics</h3>
|
17 |
+
<div class="stat-item">
|
18 |
+
<div class="stat-label"><i>📐</i> Area (sq meters):</div>
|
19 |
+
<div id="area_sqm" class="value">Loading...</div>
|
20 |
+
</div>
|
21 |
+
<div class="stat-item">
|
22 |
+
<div class="stat-label"><i>🌍</i> Area (hectares):</div>
|
23 |
+
<div id="area_hectares" class="value">Loading...</div>
|
24 |
+
</div>
|
25 |
+
<div class="stat-item">
|
26 |
+
<div class="stat-label"><i>📍</i> Centroid:</div>
|
27 |
+
<div id="centroid" class="value">Loading...</div>
|
28 |
+
</div>
|
29 |
+
<div class="stat-item">
|
30 |
+
<span class="stat-label"><i>Summary:</i></span>
|
31 |
+
<div class="value" id="summary">Loading...</div>
|
32 |
+
</div>
|
33 |
+
</div>
|
34 |
+
|
35 |
+
<footer>© 2024 GeoJSON Map Viewer by Sustainability Lab IIT Gandhinagar</footer>
|
36 |
+
<script src="geostats.js"></script> <!-- Custom script for area and centroid calculations -->
|
37 |
+
|
38 |
+
<script type="text/javascript">
|
39 |
+
// Initialize the map
|
40 |
+
const map = L.map('map').setView([58.4, 43.0], 11);
|
41 |
+
|
42 |
+
// Add a base map layer
|
43 |
+
new L.TileLayer('http://mt1.google.com/vt/lyrs=s&x={x}&y={y}&z={z}').addTo(map);
|
44 |
+
|
45 |
+
// Function to load GeoJSON and calculate statistics
|
46 |
+
function loadGeoJSON(url) {
|
47 |
+
fetch(url)
|
48 |
+
.then(response => response.json())
|
49 |
+
.then(data => {
|
50 |
+
const geoJsonLayer = L.geoJSON(data);
|
51 |
+
geoJsonLayer.addTo(map);
|
52 |
+
|
53 |
+
// Fit map to GeoJSON bounds
|
54 |
+
map.fitBounds(geoJsonLayer.getBounds());
|
55 |
+
|
56 |
+
// Call functions from gestats.js
|
57 |
+
const areaStats = calculateTotalArea(data);
|
58 |
+
const centroid = calculateCentroid(data);
|
59 |
+
const geometryCounts = countGeometryTypes(data);
|
60 |
+
const featureNames = getFeatureNames(data);
|
61 |
+
|
62 |
+
// Display area, centroid, and geometry summary in HTML
|
63 |
+
document.getElementById('area_sqm').textContent = areaStats.areaSqMeters.toFixed(2);
|
64 |
+
document.getElementById('area_hectares').textContent = areaStats.areaHectares.toFixed(2);
|
65 |
+
document.getElementById('centroid').textContent =
|
66 |
+
`Lat: ${centroid.geometry.coordinates[1].toFixed(5)},
|
67 |
+
Lng: ${centroid.geometry.coordinates[0].toFixed(5)}`;
|
68 |
+
document.getElementById('summary').innerHTML = `
|
69 |
+
<strong>Polygons:</strong> ${geometryCounts.Polygon} <br>
|
70 |
+
<strong>Points:</strong> ${geometryCounts.Point} <br>
|
71 |
+
<strong>LineStrings:</strong> ${geometryCounts.LineString} <br>
|
72 |
+
<strong>MultiPolygons:</strong> ${geometryCounts.MultiPolygon} <br>
|
73 |
+
<strong>Feature Names:</strong> ${featureNames.length > 0 ? featureNames.join(', ') : 'N/A'}
|
74 |
+
`;
|
75 |
+
})
|
76 |
+
.catch(error => {
|
77 |
+
console.error('Error loading GeoJSON:', error);
|
78 |
+
alert('Failed to load GeoJSON file. Please check the URL.');
|
79 |
+
});
|
80 |
+
}
|
81 |
+
|
82 |
+
// Get the GeoJSON URL from the query parameter 'geojson_url'
|
83 |
+
const urlParams = new URLSearchParams(window.location.search);
|
84 |
+
const geojsonUrl = urlParams.get('geojson_url');
|
85 |
+
|
86 |
+
// Load GeoJSON if a URL is provided
|
87 |
+
if (geojsonUrl) {
|
88 |
+
loadGeoJSON(geojsonUrl);
|
89 |
+
} else {
|
90 |
+
alert('Please provide a valid GeoJSON URL in the "geojson_url" query parameter.');
|
91 |
+
}
|
92 |
+
</script>
|
93 |
+
</body>
|
94 |
+
</html>
|
geostats.js
ADDED
@@ -0,0 +1,52 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
// Function to calculate total area in square meters for polygons and multipolygons
|
2 |
+
function calculateTotalArea(data) {
|
3 |
+
let totalAreaSqMeters = 0;
|
4 |
+
|
5 |
+
data.features.forEach(feature => {
|
6 |
+
if (feature.geometry.type === "Polygon" || feature.geometry.type === "MultiPolygon") {
|
7 |
+
totalAreaSqMeters += turf.area(feature);
|
8 |
+
}
|
9 |
+
});
|
10 |
+
|
11 |
+
return {
|
12 |
+
areaSqMeters: totalAreaSqMeters,
|
13 |
+
areaHectares: totalAreaSqMeters / 10000 // Convert to hectares
|
14 |
+
};
|
15 |
+
}
|
16 |
+
|
17 |
+
// Function to calculate the centroid of all polygons and multipolygons
|
18 |
+
function calculateCentroid(data) {
|
19 |
+
const polygonsOnly = {
|
20 |
+
type: "FeatureCollection",
|
21 |
+
features: data.features.filter(f => f.geometry.type === "Polygon" || f.geometry.type === "MultiPolygon")
|
22 |
+
};
|
23 |
+
|
24 |
+
return turf.centroid(polygonsOnly);
|
25 |
+
}
|
26 |
+
|
27 |
+
// Function to count different geometry types
|
28 |
+
function countGeometryTypes(data) {
|
29 |
+
const geometryCounts = { Polygon: 0, Point: 0, LineString: 0, MultiPolygon: 0 };
|
30 |
+
|
31 |
+
data.features.forEach(feature => {
|
32 |
+
const geomType = feature.geometry.type;
|
33 |
+
if (geometryCounts[geomType] !== undefined) {
|
34 |
+
geometryCounts[geomType]++;
|
35 |
+
}
|
36 |
+
});
|
37 |
+
|
38 |
+
return geometryCounts;
|
39 |
+
}
|
40 |
+
|
41 |
+
// Function to retrieve feature names if available
|
42 |
+
function getFeatureNames(data) {
|
43 |
+
const featureNames = [];
|
44 |
+
|
45 |
+
data.features.forEach(feature => {
|
46 |
+
if (feature.properties && feature.properties.name) {
|
47 |
+
featureNames.push(feature.properties.name);
|
48 |
+
}
|
49 |
+
});
|
50 |
+
|
51 |
+
return featureNames;
|
52 |
+
}
|
kml.html
ADDED
@@ -0,0 +1,55 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<!DOCTYPE html>
|
2 |
+
<html>
|
3 |
+
<head>
|
4 |
+
<title>Dynamic KML Map</title>
|
5 |
+
<link rel="stylesheet" href="https://unpkg.com/[email protected]/dist/leaflet.css" />
|
6 |
+
<script src="https://unpkg.com/[email protected]/dist/leaflet.js"></script>
|
7 |
+
<script src="./L.KML.js"></script> <!-- Make sure this file is in the repo -->
|
8 |
+
</head>
|
9 |
+
<body>
|
10 |
+
<div style="width: 100vw; height: 100vh" id="map"></div>
|
11 |
+
<script type="text/javascript">
|
12 |
+
// Helper function to get URL parameters
|
13 |
+
function getParameterByName(name, url = window.location.href) {
|
14 |
+
name = name.replace(/[\[\]]/g, '\\$&');
|
15 |
+
const regex = new RegExp('[?&]' + name + '(=([^&#]*)|&|#|$)');
|
16 |
+
const results = regex.exec(url);
|
17 |
+
if (!results) return null;
|
18 |
+
if (!results[2]) return '';
|
19 |
+
return decodeURIComponent(results[2].replace(/\+/g, ' '));
|
20 |
+
}
|
21 |
+
|
22 |
+
// Get the KML URL from the URL parameter
|
23 |
+
const kmlUrl = getParameterByName('kml_url');
|
24 |
+
|
25 |
+
if (kmlUrl) {
|
26 |
+
// Initialize map
|
27 |
+
const map = new L.Map('map', { center: new L.LatLng(58.4, 43.0), zoom: 11 });
|
28 |
+
//const osm = new L.TileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png');
|
29 |
+
//const osm = new L.TileLayer('https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}');
|
30 |
+
const osm = new L.TileLayer('http://mt1.google.com/vt/lyrs=s&x={x}&y={y}&z={z}');
|
31 |
+
map.addLayer(osm);
|
32 |
+
|
33 |
+
// Load the KML file from the given URL
|
34 |
+
fetch(kmlUrl)
|
35 |
+
.then(res => res.text())
|
36 |
+
.then(kmltext => {
|
37 |
+
const parser = new DOMParser();
|
38 |
+
const kml = parser.parseFromString(kmltext, 'text/xml');
|
39 |
+
const track = new L.KML(kml);
|
40 |
+
map.addLayer(track);
|
41 |
+
|
42 |
+
// Adjust map to show the KML
|
43 |
+
const bounds = track.getBounds();
|
44 |
+
map.fitBounds(bounds);
|
45 |
+
})
|
46 |
+
.catch(error => {
|
47 |
+
console.error('Error loading KML:', error);
|
48 |
+
alert('Failed to load KML file. Please check the URL.');
|
49 |
+
});
|
50 |
+
} else {
|
51 |
+
alert('No KML URL provided. Please use the "kml_url" parameter in the URL.');
|
52 |
+
}
|
53 |
+
</script>
|
54 |
+
</body>
|
55 |
+
</html>
|
streamlit-app.py
ADDED
@@ -0,0 +1,93 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import streamlit as st
|
2 |
+
import folium
|
3 |
+
from streamlit_folium import st_folium
|
4 |
+
import gdown
|
5 |
+
import os
|
6 |
+
import json
|
7 |
+
import requests
|
8 |
+
|
9 |
+
# Function to download from Google Drive
|
10 |
+
def download_from_gdrive(gdrive_url, output_path="temp_file"):
|
11 |
+
# Extract the file ID from the URL
|
12 |
+
file_id = gdrive_url.split('/d/')[1].split('/')[0]
|
13 |
+
gdrive_download_url = f"https://drive.google.com/uc?id={file_id}"
|
14 |
+
gdown.download(gdrive_download_url, output_path, quiet=False)
|
15 |
+
return output_path
|
16 |
+
|
17 |
+
# Title
|
18 |
+
st.title("KML/GeoJSON Viewer with Satellite Basemap")
|
19 |
+
|
20 |
+
# Get URL from Streamlit's query parameters
|
21 |
+
query_params = st.experimental_get_query_params()
|
22 |
+
file_url = query_params.get("file_url", [None])[0]
|
23 |
+
|
24 |
+
if file_url:
|
25 |
+
# Check if it's a Google Drive link
|
26 |
+
if "drive.google.com" in file_url:
|
27 |
+
# Download the file locally
|
28 |
+
local_file_path = download_from_gdrive(file_url)
|
29 |
+
file_extension = local_file_path.split('.')[-1]
|
30 |
+
else:
|
31 |
+
# Standard HTTP URL for direct access
|
32 |
+
try:
|
33 |
+
response = requests.get(file_url)
|
34 |
+
response.raise_for_status() # Ensure URL is valid
|
35 |
+
file_extension = file_url.split('.')[-1]
|
36 |
+
|
37 |
+
# Save the content to a local file
|
38 |
+
with open("temp_file." + file_extension, 'wb') as file:
|
39 |
+
file.write(response.content)
|
40 |
+
local_file_path = "temp_file." + file_extension
|
41 |
+
except requests.exceptions.RequestException as e:
|
42 |
+
st.error(f"Error loading file: {e}")
|
43 |
+
local_file_path = None
|
44 |
+
|
45 |
+
if local_file_path:
|
46 |
+
# Create map without default tiles
|
47 |
+
m = folium.Map(tiles=None)
|
48 |
+
|
49 |
+
# Add Google Maps basemap
|
50 |
+
folium.TileLayer(
|
51 |
+
tiles='https://mt1.google.com/vt/lyrs=r&x={x}&y={y}&z={z}',
|
52 |
+
attr='Google Maps',
|
53 |
+
name='Google Maps',
|
54 |
+
overlay=True,
|
55 |
+
control=True
|
56 |
+
).add_to(m)
|
57 |
+
|
58 |
+
# Add Google Satellite basemap
|
59 |
+
folium.TileLayer(
|
60 |
+
tiles='https://mt1.google.com/vt/lyrs=s&x={x}&y={y}&z={z}',
|
61 |
+
attr='Google Satellite',
|
62 |
+
name='Google Satellite',
|
63 |
+
overlay=True,
|
64 |
+
control=True
|
65 |
+
).add_to(m)
|
66 |
+
|
67 |
+
# Determine if file is KML or GeoJSON and add accordingly
|
68 |
+
if file_extension.lower() == 'kml':
|
69 |
+
folium.Kml(local_file_path).add_to(m)
|
70 |
+
elif file_extension.lower() == 'geojson':
|
71 |
+
with open(local_file_path, 'r') as file:
|
72 |
+
geojson_data = json.load(file)
|
73 |
+
folium.GeoJson(geojson_data, name="GeoJSON Data").add_to(m)
|
74 |
+
else:
|
75 |
+
st.error("Unsupported file format. Please provide a KML or GeoJSON file.")
|
76 |
+
local_file_path = None
|
77 |
+
|
78 |
+
# Center and zoom on the layer
|
79 |
+
if local_file_path:
|
80 |
+
bounds = m.get_bounds() # Adjusting the view to include KML or GeoJSON bounds
|
81 |
+
m.fit_bounds(bounds)
|
82 |
+
|
83 |
+
# Add a layer control to switch between basemaps
|
84 |
+
folium.LayerControl().add_to(m)
|
85 |
+
|
86 |
+
# Display the map in Streamlit
|
87 |
+
st_folium(m, width=700, height=500)
|
88 |
+
|
89 |
+
# Clean up the downloaded file after use
|
90 |
+
os.remove(local_file_path)
|
91 |
+
|
92 |
+
else:
|
93 |
+
st.warning("Please provide a KML or GeoJSON URL as a query parameter, e.g., `?file_url=<your_file_url>`")
|
style.css
ADDED
@@ -0,0 +1,86 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/* Custom styling for GeoJSON viewer */
|
2 |
+
body, html {
|
3 |
+
margin: 0;
|
4 |
+
padding: 0;
|
5 |
+
width: 100vw;
|
6 |
+
height: 100vh;
|
7 |
+
font-family: 'Arial', sans-serif;
|
8 |
+
background-color: #e8eef1;
|
9 |
+
display: flex;
|
10 |
+
flex-direction: column;
|
11 |
+
align-items: center;
|
12 |
+
}
|
13 |
+
|
14 |
+
header {
|
15 |
+
width: 100%;
|
16 |
+
padding: 20px;
|
17 |
+
text-align: center;
|
18 |
+
background-color: #008080;
|
19 |
+
color: #fff;
|
20 |
+
font-size: 1.8em;
|
21 |
+
font-weight: bold;
|
22 |
+
box-shadow: 0px 4px 8px rgba(0, 0, 0, 0.2);
|
23 |
+
}
|
24 |
+
|
25 |
+
#map {
|
26 |
+
width: 95%;
|
27 |
+
height: 70vh;
|
28 |
+
margin-top: 10px;
|
29 |
+
box-shadow: 0px 4px 8px rgba(0, 0, 0, 0.1);
|
30 |
+
border-radius: 8px;
|
31 |
+
}
|
32 |
+
|
33 |
+
#stats {
|
34 |
+
width: 95%;
|
35 |
+
max-width: 800px;
|
36 |
+
margin: 20px auto;
|
37 |
+
padding: 20px;
|
38 |
+
background-color: #fff;
|
39 |
+
border-radius: 8px;
|
40 |
+
box-shadow: 0px 4px 12px rgba(0, 0, 0, 0.15);
|
41 |
+
}
|
42 |
+
|
43 |
+
#stats h3 {
|
44 |
+
font-size: 1.6em;
|
45 |
+
color: #333;
|
46 |
+
text-align: center;
|
47 |
+
margin-bottom: 10px;
|
48 |
+
}
|
49 |
+
|
50 |
+
#stats .stat-item {
|
51 |
+
display: flex;
|
52 |
+
justify-content: space-between;
|
53 |
+
align-items: center;
|
54 |
+
padding: 8px 0;
|
55 |
+
border-bottom: 1px solid #ddd;
|
56 |
+
}
|
57 |
+
|
58 |
+
#stats .stat-item:last-child {
|
59 |
+
border-bottom: none;
|
60 |
+
}
|
61 |
+
|
62 |
+
#stats .stat-label {
|
63 |
+
display: flex;
|
64 |
+
align-items: center;
|
65 |
+
font-weight: bold;
|
66 |
+
color: #555;
|
67 |
+
}
|
68 |
+
|
69 |
+
#stats .stat-label i {
|
70 |
+
font-style: normal;
|
71 |
+
color: #008080;
|
72 |
+
margin-right: 8px;
|
73 |
+
}
|
74 |
+
|
75 |
+
.value {
|
76 |
+
color: #008080;
|
77 |
+
font-weight: bold;
|
78 |
+
font-size: 1.1em;
|
79 |
+
}
|
80 |
+
|
81 |
+
footer {
|
82 |
+
padding: 10px;
|
83 |
+
text-align: center;
|
84 |
+
color: #666;
|
85 |
+
font-size: 0.9em;
|
86 |
+
}
|