feature (shp): native shp handling

This commit is contained in:
MickaelK 2024-12-24 18:10:22 +11:00
parent c1aead5916
commit 866cda05c3
5 changed files with 2803 additions and 32 deletions

View file

@ -7,6 +7,8 @@ export function isSDK() {
}
export function urlSDK(url) {
if (url.startsWith("blob:")) return url;
const importURL = new URL(import.meta.url);
if (new RegExp("^/").test(url) === false) {
url = "/" + url;

View file

@ -5,6 +5,7 @@ import { isSDK, urlSDK } from "../helpers/sdk.js";
export default function(opts) {
if (typeof opts === "string") opts = { url: opts, withCredentials: true };
else if (typeof opts !== "object") throw new Error("unsupported call");
if (!opts.headers) opts.headers = {};
opts.headers["X-Requested-With"] = "XmlHttpRequest";
if (window.BEARER_TOKEN) opts.headers["Authorization"] = `Bearer ${window.BEARER_TOKEN}`;
@ -14,13 +15,17 @@ export default function(opts) {
opts.url = urlSDK(opts.url);
}
return ajax({ withCredentials: true, ...opts, responseType: "text" }).pipe(
const responseType = opts.responseType === "json" ? "text" : opts.responseType;
return ajax({
withCredentials: true,
...opts,
responseType,
}).pipe(
rxjs.map((res) => {
const result = res.xhr.responseText;
if (opts.responseType === "json") {
const json = JSON.parse(result);
res.responseJSON = json;
if (json.status !== "ok") {
const result = res.xhr.responseText;
res.responseJSON = JSON.parse(result);
if (res.responseJSON.status !== "ok") {
throw new AjaxError("Oups something went wrong", result);
}
}
@ -31,6 +36,14 @@ export default function(opts) {
}
function processError(xhr, err) {
let responseText = "";
try {
responseText = xhr?.responseText;
} catch (err) {
if (err.name === "InvalidStateError") {} // InvalidStateError: Failed to read the 'responseText' property from 'XMLHttpRequest': The value is only accessible if the object's 'responseType' is '' or 'text' (was 'arraybuffer').
else throw err;
}
const response = (function(content) {
let message = content;
try {
@ -46,10 +59,9 @@ function processError(xhr, err) {
};
}
return message || { message: "empty response" };
})(xhr?.responseText || "");
})(responseText);
const message = response.message || null;
if (window.navigator.onLine === false) {
return new AjaxError("Connection Lost", err, "NO_INTERNET");
}
@ -85,14 +97,14 @@ function processError(xhr, err) {
err, "CONFLICT"
);
case 0:
switch (xhr?.responseText) {
switch (responseText) {
case "":
return new AjaxError(
"Service unavailable, if the problem persist, contact your administrator",
err, "INTERNAL_SERVER_ERROR"
);
default:
return new AjaxError(xhr.responseText, err, "INTERNAL_SERVER_ERROR");
return new AjaxError(responseText, err, "INTERNAL_SERVER_ERROR");
}
default:
return new AjaxError(message || "Oups something went wrong", err);

File diff suppressed because it is too large Load diff

View file

@ -1,12 +1,13 @@
import { createElement } from "../../lib/skeleton/index.js";
import rxjs from "../../lib/rx.js";
import { createElement, nop } from "../../lib/skeleton/index.js";
import rxjs, { effect } from "../../lib/rx.js";
import { qs } from "../../lib/dom.js";
import { extname } from "../../lib/path.js";
import ajax from "../../lib/ajax.js";
import { loadCSS, loadJS } from "../../helpers/loader.js";
import { getFilename, getDownloadUrl } from "./common.js";
import { createLoader } from "../../components/loader.js";
import { renderMenubar, buttonDownload } from "./component_menubar.js";
import { cat } from "./model_files.js";
import ctrlError from "../ctrl_error.js";
const DEFAULT_TILE_SERVER = "https://tile.openstreetmap.org/{z}/{x}/{y}.png"; // "https://cartodb-basemaps-a.global.ssl.fastly.net/light_all/{z}/{x}/{y}.png",
@ -15,10 +16,10 @@ const PLUGINS = [
// ... add more plugins via frontend override
];
export default async function(render) {
export default async function(render, { mime, getDownloadUrl = nop, getFilename = nop }) {
const $page = createElement(`
<div class="component_map">
<component-menubar></component-menubar>
<component-menubar filename="${getFilename() || ""}"></component-menubar>
<div id="map"></div>
</div>
`);
@ -29,20 +30,18 @@ export default async function(render) {
);
const map = window.L.map("map");
const fileview = [getFilename()];
for (let i=0; i<fileview.length; i++) {
await cat(fileview[i]).pipe(rxjs.mergeMap(async(content) => {
switch (extname(fileview[i])) {
case "geojson":
return loadGeoJSON(map, content);
case "wms":
return loadWMS(map, content);
default:
throw new Error("Not Supported");
}
})).toPromise();
}
const removeLoader = createLoader(qs($page, "#map"));
await effect(ajax({ url: getDownloadUrl(), responseType: "arraybuffer" }).pipe(
rxjs.map(({ response }) => response),
rxjs.mergeMap(async(data) => { switch(mime) {
case "application/geo+json": return loadGeoJSON(map, JSON.parse(new TextDecoder().decode(data)));
case "application/vnd.ogc.wms_xml": return loadWMS(map, new TextDecoder().decode(data));
case "application/vnd.shp": return await loadSHP(map, data);
default: throw new Error(`Insupported mime type: '${mime}'`);
}}),
removeLoader,
rxjs.catchError(ctrlError()),
));
for (let i=0; i<PLUGINS.length; i++) {
await import(`./application_map/${PLUGINS[i]}.js`)
@ -50,11 +49,17 @@ export default async function(render) {
}
}
export async function init() {
export async function init($root) {
const priors = ($root && [
$root.classList.add("component_page_viewerpage"),
loadCSS(import.meta.url, "./component_menubar.css"),
loadCSS(import.meta.url, "../ctrl_viewerpage.css"),
]);
await Promise.all([
loadJS(import.meta.url, "../../lib/vendor/leaflet/leaflet.js"),
loadCSS(import.meta.url, "../../lib/vendor/leaflet/leaflet.css"),
loadCSS(import.meta.url, "./application_map.css"),
...priors,
]);
}
@ -63,7 +68,7 @@ function loadGeoJSON(map, content) {
const overlay = { global: window.L.layerGroup([]) };
let n = 0;
const geojson = window.L.geoJSON(JSON.parse(content), {
const geojson = window.L.geoJSON(content, {
onEachFeature: (feature, shape) => {
n += 1;
if (n > 10000) return;
@ -129,3 +134,13 @@ function loadWMS(map, content) {
window.L.tileLayer.wms(svcURL, { layers: _layer }).addTo(map);
window.L.control.layers(baseLayers, {}).addTo(map);
}
async function loadSHP(map, content) {
const module = await import("../../lib/vendor/shp-to-geojson.browser.js");
const shp = new module.default({});
shp._shpBuffer = module.Buffer.from(content);
shp._tableBuffer = module.Buffer.from(new ArrayBuffer(16));
shp._init();
await shp.load();
loadGeoJSON(map, shp.getGeoJson());
}

View file

@ -24,7 +24,7 @@ export function opener(file = "", mimes) {
return ["audio", { mime }];
} else if (mime === "application/x-form") {
return ["form", { mime }];
} else if (mime === "application/geo+json" || mime === "application/vnd.ogc.wms_xml") {
} else if (mime === "application/geo+json" || mime === "application/vnd.ogc.wms_xml" || mime === "application/vnd.shp") {
return ["map", { mime }];
} else if (type === "video" || mime === "application/ogg") {
return ["video", { mime }];