mirror of
https://gitlab.com/wgp/dougal/software.git
synced 2025-12-06 12:57:08 +00:00
Compare commits
31 Commits
131-show-m
...
75-quality
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
57a08c93bc | ||
|
|
fabc9fe757 | ||
|
|
6f32f24481 | ||
|
|
dffe7defbb | ||
|
|
b9844528f1 | ||
|
|
cd78dbd0d8 | ||
|
|
798203be9f | ||
|
|
5bfd7dc835 | ||
|
|
c17862fbbb | ||
|
|
04c0369923 | ||
|
|
026cfb6f98 | ||
|
|
a4e6ec0712 | ||
|
|
b3e052cb12 | ||
|
|
cf88ecf172 | ||
|
|
e267440711 | ||
|
|
454094b187 | ||
|
|
862e754a6f | ||
|
|
894877750e | ||
|
|
09b45d5d65 | ||
|
|
1352c3b312 | ||
|
|
30aa2c302e | ||
|
|
3eaa2757b9 | ||
|
|
6f6af1bbc7 | ||
|
|
019561229c | ||
|
|
e212dc8b92 | ||
|
|
5c00013892 | ||
|
|
1e5bdcc068 | ||
|
|
a280a910f5 | ||
|
|
45fe467a21 | ||
|
|
8d3b7adc78 | ||
|
|
079d3a18b0 |
91
etc/db/upgrades/upgrade07-81d9ea19→0a10c897.sql
Normal file
91
etc/db/upgrades/upgrade07-81d9ea19→0a10c897.sql
Normal file
@@ -0,0 +1,91 @@
|
||||
-- Upgrade the database from commit 81d9ea19 to 0a10c897.
|
||||
--
|
||||
-- NOTE: This upgrade must be applied to every schema in the database.
|
||||
-- NOTE: Each application starts a transaction, which must be committed
|
||||
-- or rolled back.
|
||||
--
|
||||
-- This defines a new function ij_error(line, point, geometry) which
|
||||
-- returns the crossline and inline distance (in metres) between the
|
||||
-- geometry (which must be a point) and the preplot corresponding to
|
||||
-- line / point.
|
||||
--
|
||||
-- To apply, run as the dougal user, for every schema in the database:
|
||||
--
|
||||
-- psql <<EOF
|
||||
-- SET search_path TO survey_*,public;
|
||||
-- \i $THIS_FILE
|
||||
-- COMMIT;
|
||||
-- EOF
|
||||
--
|
||||
-- NOTE: It can be applied multiple times without ill effect.
|
||||
|
||||
|
||||
BEGIN;
|
||||
|
||||
|
||||
-- Return the crossline, inline error of `geom` with respect to `line` and `point`
|
||||
-- in the project's binning grid.
|
||||
|
||||
CREATE OR REPLACE FUNCTION ij_error(line double precision, point double precision, geom public.geometry)
|
||||
RETURNS public.geometry(Point, 0)
|
||||
LANGUAGE plpgsql STABLE LEAKPROOF
|
||||
AS $$
|
||||
DECLARE
|
||||
bp jsonb := binning_parameters();
|
||||
ij public.geometry := to_binning_grid(geom, bp);
|
||||
|
||||
theta numeric := (bp->>'theta')::numeric * pi() / 180;
|
||||
I_inc numeric DEFAULT 1;
|
||||
J_inc numeric DEFAULT 1;
|
||||
I_width numeric := (bp->>'I_width')::numeric;
|
||||
J_width numeric := (bp->>'J_width')::numeric;
|
||||
|
||||
a numeric := (I_inc/I_width) * cos(theta);
|
||||
b numeric := (I_inc/I_width) * -sin(theta);
|
||||
c numeric := (J_inc/J_width) * sin(theta);
|
||||
d numeric := (J_inc/J_width) * cos(theta);
|
||||
xoff numeric := (bp->'origin'->>'I')::numeric;
|
||||
yoff numeric := (bp->'origin'->>'J')::numeric;
|
||||
E0 numeric := (bp->'origin'->>'easting')::numeric;
|
||||
N0 numeric := (bp->'origin'->>'northing')::numeric;
|
||||
|
||||
error_i double precision;
|
||||
error_j double precision;
|
||||
BEGIN
|
||||
error_i := (public.st_x(ij) - line) * I_width;
|
||||
error_j := (public.st_y(ij) - point) * J_width;
|
||||
|
||||
RETURN public.ST_MakePoint(error_i, error_j);
|
||||
END
|
||||
$$;
|
||||
|
||||
|
||||
-- Return the list of points and metadata for all sequences.
|
||||
-- Only points which have a corresponding preplot are returned.
|
||||
-- If available, final positions are returned as well, if not they
|
||||
-- are NULL.
|
||||
-- Likewise, crossline / inline errors are also returned as a PostGIS
|
||||
-- 2D point both for raw and final data.
|
||||
|
||||
CREATE OR REPLACE VIEW sequences_detail AS
|
||||
SELECT
|
||||
rl.sequence, rl.line AS sailline,
|
||||
rs.line, rs.point,
|
||||
rs.tstamp,
|
||||
rs.objref objRefRaw, fs.objref objRefFinal,
|
||||
ST_Transform(pp.geometry, 4326) geometryPreplot,
|
||||
ST_Transform(rs.geometry, 4326) geometryRaw,
|
||||
ST_Transform(fs.geometry, 4326) geometryFinal,
|
||||
ij_error(rs.line, rs.point, rs.geometry) errorRaw,
|
||||
ij_error(rs.line, rs.point, fs.geometry) errorFinal,
|
||||
json_build_object('preplot', pp.meta, 'raw', rs.meta, 'final', fs.meta) meta
|
||||
FROM
|
||||
raw_lines rl
|
||||
INNER JOIN raw_shots rs USING (sequence)
|
||||
INNER JOIN preplot_points pp ON rs.line = pp.line AND rs.point = pp.point
|
||||
LEFT JOIN final_shots fs ON rl.sequence = fs.sequence AND rs.point = fs.point;
|
||||
|
||||
|
||||
--
|
||||
--NOTE Run `COMMIT;` now if all went well
|
||||
--
|
||||
734
lib/www/client/source/package-lock.json
generated
734
lib/www/client/source/package-lock.json
generated
@@ -11,12 +11,14 @@
|
||||
"dependencies": {
|
||||
"@mdi/font": "^5.6.55",
|
||||
"core-js": "^3.6.5",
|
||||
"d3": "^7.0.1",
|
||||
"jwt-decode": "^3.0.0",
|
||||
"leaflet": "^1.7.1",
|
||||
"leaflet-arrowheads": "^1.2.2",
|
||||
"leaflet-realtime": "^2.2.0",
|
||||
"leaflet.markercluster": "^1.4.1",
|
||||
"marked": "^2.0.3",
|
||||
"plotly.js-dist": "^2.5.0",
|
||||
"suncalc": "^1.8.0",
|
||||
"typeface-roboto": "0.0.75",
|
||||
"vue": "^2.6.12",
|
||||
@@ -4296,6 +4298,395 @@
|
||||
"integrity": "sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/d3": {
|
||||
"version": "7.0.1",
|
||||
"resolved": "https://registry.npmjs.org/d3/-/d3-7.0.1.tgz",
|
||||
"integrity": "sha512-74zonD4nAtxF9dtwFwJ3RuoHPh2D/UTFX26midBuMVH+7pRbOezuyLUIb8mbQMuYFlcUXT+xy++orCmnvMM/CA==",
|
||||
"dependencies": {
|
||||
"d3-array": "3",
|
||||
"d3-axis": "3",
|
||||
"d3-brush": "3",
|
||||
"d3-chord": "3",
|
||||
"d3-color": "3",
|
||||
"d3-contour": "3",
|
||||
"d3-delaunay": "6",
|
||||
"d3-dispatch": "3",
|
||||
"d3-drag": "3",
|
||||
"d3-dsv": "3",
|
||||
"d3-ease": "3",
|
||||
"d3-fetch": "3",
|
||||
"d3-force": "3",
|
||||
"d3-format": "3",
|
||||
"d3-geo": "3",
|
||||
"d3-hierarchy": "3",
|
||||
"d3-interpolate": "3",
|
||||
"d3-path": "3",
|
||||
"d3-polygon": "3",
|
||||
"d3-quadtree": "3",
|
||||
"d3-random": "3",
|
||||
"d3-scale": "4",
|
||||
"d3-scale-chromatic": "3",
|
||||
"d3-selection": "3",
|
||||
"d3-shape": "3",
|
||||
"d3-time": "3",
|
||||
"d3-time-format": "4",
|
||||
"d3-timer": "3",
|
||||
"d3-transition": "3",
|
||||
"d3-zoom": "3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-array": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.0.2.tgz",
|
||||
"integrity": "sha512-nTN4OC6ufZueotlexbxBd2z8xmG1eIfhvP2m1auH2ONps0L+AZn1r0JWuzMXZ6XgOj1VBOp7GGZmEs9NUFEBbA==",
|
||||
"dependencies": {
|
||||
"internmap": "1 - 2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-axis": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-3.0.0.tgz",
|
||||
"integrity": "sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-brush": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-3.0.0.tgz",
|
||||
"integrity": "sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==",
|
||||
"dependencies": {
|
||||
"d3-dispatch": "1 - 3",
|
||||
"d3-drag": "2 - 3",
|
||||
"d3-interpolate": "1 - 3",
|
||||
"d3-selection": "3",
|
||||
"d3-transition": "3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-chord": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-3.0.1.tgz",
|
||||
"integrity": "sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==",
|
||||
"dependencies": {
|
||||
"d3-path": "1 - 3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-color": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.0.1.tgz",
|
||||
"integrity": "sha512-6/SlHkDOBLyQSJ1j1Ghs82OIUXpKWlR0hCsw0XrLSQhuUPuCSmLQ1QPH98vpnQxMUQM2/gfAkUEWsupVpd9JGw==",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-contour": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-3.0.1.tgz",
|
||||
"integrity": "sha512-0Oc4D0KyhwhM7ZL0RMnfGycLN7hxHB8CMmwZ3+H26PWAG0ozNuYG5hXSDNgmP1SgJkQMrlG6cP20HoaSbvcJTQ==",
|
||||
"dependencies": {
|
||||
"d3-array": "2 - 3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-delaunay": {
|
||||
"version": "6.0.2",
|
||||
"resolved": "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-6.0.2.tgz",
|
||||
"integrity": "sha512-IMLNldruDQScrcfT+MWnazhHbDJhcRJyOEBAJfwQnHle1RPh6WDuLvxNArUju2VSMSUuKlY5BGHRJ2cYyoFLQQ==",
|
||||
"dependencies": {
|
||||
"delaunator": "5"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-dispatch": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz",
|
||||
"integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-drag": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz",
|
||||
"integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==",
|
||||
"dependencies": {
|
||||
"d3-dispatch": "1 - 3",
|
||||
"d3-selection": "3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-dsv": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-3.0.1.tgz",
|
||||
"integrity": "sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==",
|
||||
"dependencies": {
|
||||
"commander": "7",
|
||||
"iconv-lite": "0.6",
|
||||
"rw": "1"
|
||||
},
|
||||
"bin": {
|
||||
"csv2json": "bin/dsv2json.js",
|
||||
"csv2tsv": "bin/dsv2dsv.js",
|
||||
"dsv2dsv": "bin/dsv2dsv.js",
|
||||
"dsv2json": "bin/dsv2json.js",
|
||||
"json2csv": "bin/json2dsv.js",
|
||||
"json2dsv": "bin/json2dsv.js",
|
||||
"json2tsv": "bin/json2dsv.js",
|
||||
"tsv2csv": "bin/dsv2dsv.js",
|
||||
"tsv2json": "bin/dsv2json.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-dsv/node_modules/commander": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",
|
||||
"integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==",
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-dsv/node_modules/iconv-lite": {
|
||||
"version": "0.6.3",
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
|
||||
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
|
||||
"dependencies": {
|
||||
"safer-buffer": ">= 2.1.2 < 3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-ease": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz",
|
||||
"integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-fetch": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/d3-fetch/-/d3-fetch-3.0.1.tgz",
|
||||
"integrity": "sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==",
|
||||
"dependencies": {
|
||||
"d3-dsv": "1 - 3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-force": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-force/-/d3-force-3.0.0.tgz",
|
||||
"integrity": "sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==",
|
||||
"dependencies": {
|
||||
"d3-dispatch": "1 - 3",
|
||||
"d3-quadtree": "1 - 3",
|
||||
"d3-timer": "1 - 3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-format": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.0.1.tgz",
|
||||
"integrity": "sha512-hdL7+HBIohpgfolhBxr1KX47VMD6+vVD/oEFrxk5yhmzV2prk99EkFKYpXuhVkFpTgHdJ6/4bYcjdLPPXV4tIA==",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-geo": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.0.1.tgz",
|
||||
"integrity": "sha512-Wt23xBych5tSy9IYAM1FR2rWIBFWa52B/oF/GYe5zbdHrg08FU8+BuI6X4PvTwPDdqdAdq04fuWJpELtsaEjeA==",
|
||||
"dependencies": {
|
||||
"d3-array": "2.5.0 - 3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-hierarchy": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-3.0.1.tgz",
|
||||
"integrity": "sha512-RlLTaofEoOrMK1JoXYIGhKTkJFI/6rFrYPgxy6QlZo2BcVc4HGTqEU0rPpzuMq5T/5XcMtAzv1XiLA3zRTfygw==",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-interpolate": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz",
|
||||
"integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==",
|
||||
"dependencies": {
|
||||
"d3-color": "1 - 3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-path": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.0.1.tgz",
|
||||
"integrity": "sha512-gq6gZom9AFZby0YLduxT1qmrp4xpBA1YZr19OI717WIdKE2OM5ETq5qrHLb301IgxhLwcuxvGZVLeeWc/k1I6w==",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-polygon": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-3.0.1.tgz",
|
||||
"integrity": "sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-quadtree": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-3.0.1.tgz",
|
||||
"integrity": "sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-random": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/d3-random/-/d3-random-3.0.1.tgz",
|
||||
"integrity": "sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-scale": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.0.tgz",
|
||||
"integrity": "sha512-foHQYKpWQcyndH1CGoHdUC4PECxTxonzwwBXGT8qu+Drb1FIc6ON6dG2P5f4hRRMkLiIKeWK7iFtdznDUrnuPQ==",
|
||||
"dependencies": {
|
||||
"d3-array": "2.10.0 - 3",
|
||||
"d3-format": "1 - 3",
|
||||
"d3-interpolate": "1.2.0 - 3",
|
||||
"d3-time": "2.1.1 - 3",
|
||||
"d3-time-format": "2 - 4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-scale-chromatic": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-3.0.0.tgz",
|
||||
"integrity": "sha512-Lx9thtxAKrO2Pq6OO2Ua474opeziKr279P/TKZsMAhYyNDD3EnCffdbgeSYN5O7m2ByQsxtuP2CSDczNUIZ22g==",
|
||||
"dependencies": {
|
||||
"d3-color": "1 - 3",
|
||||
"d3-interpolate": "1 - 3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-selection": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz",
|
||||
"integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-shape": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.0.1.tgz",
|
||||
"integrity": "sha512-HNZNEQoDhuCrDWEc/BMbF/hKtzMZVoe64TvisFLDp2Iyj0UShB/E6/lBsLlJTfBMbYgftHj90cXJ0SEitlE6Xw==",
|
||||
"dependencies": {
|
||||
"d3-path": "1 - 3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-time": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.0.0.tgz",
|
||||
"integrity": "sha512-zmV3lRnlaLI08y9IMRXSDshQb5Nj77smnfpnd2LrBa/2K281Jijactokeak14QacHs/kKq0AQ121nidNYlarbQ==",
|
||||
"dependencies": {
|
||||
"d3-array": "2 - 3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-time-format": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.0.0.tgz",
|
||||
"integrity": "sha512-nzaCwlj+ZVBIlFuVOT1RmU+6xb/7D5IcnhHzHQcBgS/aTa5K9fWZNN5LCXA27LgF5WxoSNJqKBbLcGMtM6Ca6A==",
|
||||
"dependencies": {
|
||||
"d3-time": "1 - 3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-timer": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz",
|
||||
"integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-transition": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-3.0.1.tgz",
|
||||
"integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==",
|
||||
"dependencies": {
|
||||
"d3-color": "1 - 3",
|
||||
"d3-dispatch": "1 - 3",
|
||||
"d3-ease": "1 - 3",
|
||||
"d3-interpolate": "1 - 3",
|
||||
"d3-timer": "1 - 3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"d3-selection": "2 - 3"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-zoom": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz",
|
||||
"integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==",
|
||||
"dependencies": {
|
||||
"d3-dispatch": "1 - 3",
|
||||
"d3-drag": "2 - 3",
|
||||
"d3-interpolate": "1 - 3",
|
||||
"d3-selection": "2 - 3",
|
||||
"d3-transition": "2 - 3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/dashdash": {
|
||||
"version": "1.14.1",
|
||||
"resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
|
||||
@@ -4643,6 +5034,14 @@
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/delaunator": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.0.0.tgz",
|
||||
"integrity": "sha512-AyLvtyJdbv/U1GkiS6gUUzclRoAY4Gs75qkMygJJhU75LW4DNuSF2RMzpxs9jw9Oz1BobHjTdkG3zdP55VxAqw==",
|
||||
"dependencies": {
|
||||
"robust-predicates": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/delayed-stream": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
||||
@@ -6535,6 +6934,14 @@
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/internmap": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.1.tgz",
|
||||
"integrity": "sha512-Ujwccrj9FkGqjbY3iVoxD1VV+KdZZeENx0rphrtzmRXbFvkFO88L80BL/zeSIguX/7T+y8k04xqtgWgS5vxwxw==",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/interpret": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz",
|
||||
@@ -8540,6 +8947,11 @@
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/plotly.js-dist": {
|
||||
"version": "2.5.0",
|
||||
"resolved": "https://registry.npmjs.org/plotly.js-dist/-/plotly.js-dist-2.5.0.tgz",
|
||||
"integrity": "sha512-jdxnL/+yluckc6U7zFgxC4QgTvAvKriIQ0yN/gDzTd5s3YNPuCdReHB3p91Zd0fVkq4eq84I8yp/9iBbnbXLJQ=="
|
||||
},
|
||||
"node_modules/pnp-webpack-plugin": {
|
||||
"version": "1.6.4",
|
||||
"resolved": "https://registry.npmjs.org/pnp-webpack-plugin/-/pnp-webpack-plugin-1.6.4.tgz",
|
||||
@@ -9891,6 +10303,11 @@
|
||||
"inherits": "^2.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/robust-predicates": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.1.tgz",
|
||||
"integrity": "sha512-ndEIpszUHiG4HtDsQLeIuMvRsDnn8c8rYStabochtUeCvfuvNptb5TUbVD68LRAILPX7p9nqQGh4xJgn3EHS/g=="
|
||||
},
|
||||
"node_modules/run-queue": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz",
|
||||
@@ -9900,6 +10317,11 @@
|
||||
"aproba": "^1.1.1"
|
||||
}
|
||||
},
|
||||
"node_modules/rw": {
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz",
|
||||
"integrity": "sha1-P4Yt+pGrdmsUiF700BEkv9oHT7Q="
|
||||
},
|
||||
"node_modules/safe-buffer": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
||||
@@ -9918,8 +10340,7 @@
|
||||
"node_modules/safer-buffer": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
||||
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
|
||||
"dev": true
|
||||
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
|
||||
},
|
||||
"node_modules/sass": {
|
||||
"version": "1.26.11",
|
||||
@@ -16716,6 +17137,284 @@
|
||||
"integrity": "sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=",
|
||||
"dev": true
|
||||
},
|
||||
"d3": {
|
||||
"version": "7.0.1",
|
||||
"resolved": "https://registry.npmjs.org/d3/-/d3-7.0.1.tgz",
|
||||
"integrity": "sha512-74zonD4nAtxF9dtwFwJ3RuoHPh2D/UTFX26midBuMVH+7pRbOezuyLUIb8mbQMuYFlcUXT+xy++orCmnvMM/CA==",
|
||||
"requires": {
|
||||
"d3-array": "3",
|
||||
"d3-axis": "3",
|
||||
"d3-brush": "3",
|
||||
"d3-chord": "3",
|
||||
"d3-color": "3",
|
||||
"d3-contour": "3",
|
||||
"d3-delaunay": "6",
|
||||
"d3-dispatch": "3",
|
||||
"d3-drag": "3",
|
||||
"d3-dsv": "3",
|
||||
"d3-ease": "3",
|
||||
"d3-fetch": "3",
|
||||
"d3-force": "3",
|
||||
"d3-format": "3",
|
||||
"d3-geo": "3",
|
||||
"d3-hierarchy": "3",
|
||||
"d3-interpolate": "3",
|
||||
"d3-path": "3",
|
||||
"d3-polygon": "3",
|
||||
"d3-quadtree": "3",
|
||||
"d3-random": "3",
|
||||
"d3-scale": "4",
|
||||
"d3-scale-chromatic": "3",
|
||||
"d3-selection": "3",
|
||||
"d3-shape": "3",
|
||||
"d3-time": "3",
|
||||
"d3-time-format": "4",
|
||||
"d3-timer": "3",
|
||||
"d3-transition": "3",
|
||||
"d3-zoom": "3"
|
||||
}
|
||||
},
|
||||
"d3-array": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.0.2.tgz",
|
||||
"integrity": "sha512-nTN4OC6ufZueotlexbxBd2z8xmG1eIfhvP2m1auH2ONps0L+AZn1r0JWuzMXZ6XgOj1VBOp7GGZmEs9NUFEBbA==",
|
||||
"requires": {
|
||||
"internmap": "1 - 2"
|
||||
}
|
||||
},
|
||||
"d3-axis": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-3.0.0.tgz",
|
||||
"integrity": "sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw=="
|
||||
},
|
||||
"d3-brush": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-3.0.0.tgz",
|
||||
"integrity": "sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==",
|
||||
"requires": {
|
||||
"d3-dispatch": "1 - 3",
|
||||
"d3-drag": "2 - 3",
|
||||
"d3-interpolate": "1 - 3",
|
||||
"d3-selection": "3",
|
||||
"d3-transition": "3"
|
||||
}
|
||||
},
|
||||
"d3-chord": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-3.0.1.tgz",
|
||||
"integrity": "sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==",
|
||||
"requires": {
|
||||
"d3-path": "1 - 3"
|
||||
}
|
||||
},
|
||||
"d3-color": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.0.1.tgz",
|
||||
"integrity": "sha512-6/SlHkDOBLyQSJ1j1Ghs82OIUXpKWlR0hCsw0XrLSQhuUPuCSmLQ1QPH98vpnQxMUQM2/gfAkUEWsupVpd9JGw=="
|
||||
},
|
||||
"d3-contour": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-3.0.1.tgz",
|
||||
"integrity": "sha512-0Oc4D0KyhwhM7ZL0RMnfGycLN7hxHB8CMmwZ3+H26PWAG0ozNuYG5hXSDNgmP1SgJkQMrlG6cP20HoaSbvcJTQ==",
|
||||
"requires": {
|
||||
"d3-array": "2 - 3"
|
||||
}
|
||||
},
|
||||
"d3-delaunay": {
|
||||
"version": "6.0.2",
|
||||
"resolved": "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-6.0.2.tgz",
|
||||
"integrity": "sha512-IMLNldruDQScrcfT+MWnazhHbDJhcRJyOEBAJfwQnHle1RPh6WDuLvxNArUju2VSMSUuKlY5BGHRJ2cYyoFLQQ==",
|
||||
"requires": {
|
||||
"delaunator": "5"
|
||||
}
|
||||
},
|
||||
"d3-dispatch": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz",
|
||||
"integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg=="
|
||||
},
|
||||
"d3-drag": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz",
|
||||
"integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==",
|
||||
"requires": {
|
||||
"d3-dispatch": "1 - 3",
|
||||
"d3-selection": "3"
|
||||
}
|
||||
},
|
||||
"d3-dsv": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-3.0.1.tgz",
|
||||
"integrity": "sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==",
|
||||
"requires": {
|
||||
"commander": "7",
|
||||
"iconv-lite": "0.6",
|
||||
"rw": "1"
|
||||
},
|
||||
"dependencies": {
|
||||
"commander": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",
|
||||
"integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw=="
|
||||
},
|
||||
"iconv-lite": {
|
||||
"version": "0.6.3",
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
|
||||
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
|
||||
"requires": {
|
||||
"safer-buffer": ">= 2.1.2 < 3.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"d3-ease": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz",
|
||||
"integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w=="
|
||||
},
|
||||
"d3-fetch": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/d3-fetch/-/d3-fetch-3.0.1.tgz",
|
||||
"integrity": "sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==",
|
||||
"requires": {
|
||||
"d3-dsv": "1 - 3"
|
||||
}
|
||||
},
|
||||
"d3-force": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-force/-/d3-force-3.0.0.tgz",
|
||||
"integrity": "sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==",
|
||||
"requires": {
|
||||
"d3-dispatch": "1 - 3",
|
||||
"d3-quadtree": "1 - 3",
|
||||
"d3-timer": "1 - 3"
|
||||
}
|
||||
},
|
||||
"d3-format": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.0.1.tgz",
|
||||
"integrity": "sha512-hdL7+HBIohpgfolhBxr1KX47VMD6+vVD/oEFrxk5yhmzV2prk99EkFKYpXuhVkFpTgHdJ6/4bYcjdLPPXV4tIA=="
|
||||
},
|
||||
"d3-geo": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.0.1.tgz",
|
||||
"integrity": "sha512-Wt23xBych5tSy9IYAM1FR2rWIBFWa52B/oF/GYe5zbdHrg08FU8+BuI6X4PvTwPDdqdAdq04fuWJpELtsaEjeA==",
|
||||
"requires": {
|
||||
"d3-array": "2.5.0 - 3"
|
||||
}
|
||||
},
|
||||
"d3-hierarchy": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-3.0.1.tgz",
|
||||
"integrity": "sha512-RlLTaofEoOrMK1JoXYIGhKTkJFI/6rFrYPgxy6QlZo2BcVc4HGTqEU0rPpzuMq5T/5XcMtAzv1XiLA3zRTfygw=="
|
||||
},
|
||||
"d3-interpolate": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz",
|
||||
"integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==",
|
||||
"requires": {
|
||||
"d3-color": "1 - 3"
|
||||
}
|
||||
},
|
||||
"d3-path": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.0.1.tgz",
|
||||
"integrity": "sha512-gq6gZom9AFZby0YLduxT1qmrp4xpBA1YZr19OI717WIdKE2OM5ETq5qrHLb301IgxhLwcuxvGZVLeeWc/k1I6w=="
|
||||
},
|
||||
"d3-polygon": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-3.0.1.tgz",
|
||||
"integrity": "sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg=="
|
||||
},
|
||||
"d3-quadtree": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-3.0.1.tgz",
|
||||
"integrity": "sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw=="
|
||||
},
|
||||
"d3-random": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/d3-random/-/d3-random-3.0.1.tgz",
|
||||
"integrity": "sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ=="
|
||||
},
|
||||
"d3-scale": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.0.tgz",
|
||||
"integrity": "sha512-foHQYKpWQcyndH1CGoHdUC4PECxTxonzwwBXGT8qu+Drb1FIc6ON6dG2P5f4hRRMkLiIKeWK7iFtdznDUrnuPQ==",
|
||||
"requires": {
|
||||
"d3-array": "2.10.0 - 3",
|
||||
"d3-format": "1 - 3",
|
||||
"d3-interpolate": "1.2.0 - 3",
|
||||
"d3-time": "2.1.1 - 3",
|
||||
"d3-time-format": "2 - 4"
|
||||
}
|
||||
},
|
||||
"d3-scale-chromatic": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-3.0.0.tgz",
|
||||
"integrity": "sha512-Lx9thtxAKrO2Pq6OO2Ua474opeziKr279P/TKZsMAhYyNDD3EnCffdbgeSYN5O7m2ByQsxtuP2CSDczNUIZ22g==",
|
||||
"requires": {
|
||||
"d3-color": "1 - 3",
|
||||
"d3-interpolate": "1 - 3"
|
||||
}
|
||||
},
|
||||
"d3-selection": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz",
|
||||
"integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ=="
|
||||
},
|
||||
"d3-shape": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.0.1.tgz",
|
||||
"integrity": "sha512-HNZNEQoDhuCrDWEc/BMbF/hKtzMZVoe64TvisFLDp2Iyj0UShB/E6/lBsLlJTfBMbYgftHj90cXJ0SEitlE6Xw==",
|
||||
"requires": {
|
||||
"d3-path": "1 - 3"
|
||||
}
|
||||
},
|
||||
"d3-time": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.0.0.tgz",
|
||||
"integrity": "sha512-zmV3lRnlaLI08y9IMRXSDshQb5Nj77smnfpnd2LrBa/2K281Jijactokeak14QacHs/kKq0AQ121nidNYlarbQ==",
|
||||
"requires": {
|
||||
"d3-array": "2 - 3"
|
||||
}
|
||||
},
|
||||
"d3-time-format": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.0.0.tgz",
|
||||
"integrity": "sha512-nzaCwlj+ZVBIlFuVOT1RmU+6xb/7D5IcnhHzHQcBgS/aTa5K9fWZNN5LCXA27LgF5WxoSNJqKBbLcGMtM6Ca6A==",
|
||||
"requires": {
|
||||
"d3-time": "1 - 3"
|
||||
}
|
||||
},
|
||||
"d3-timer": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz",
|
||||
"integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA=="
|
||||
},
|
||||
"d3-transition": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-3.0.1.tgz",
|
||||
"integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==",
|
||||
"requires": {
|
||||
"d3-color": "1 - 3",
|
||||
"d3-dispatch": "1 - 3",
|
||||
"d3-ease": "1 - 3",
|
||||
"d3-interpolate": "1 - 3",
|
||||
"d3-timer": "1 - 3"
|
||||
}
|
||||
},
|
||||
"d3-zoom": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz",
|
||||
"integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==",
|
||||
"requires": {
|
||||
"d3-dispatch": "1 - 3",
|
||||
"d3-drag": "2 - 3",
|
||||
"d3-interpolate": "1 - 3",
|
||||
"d3-selection": "2 - 3",
|
||||
"d3-transition": "2 - 3"
|
||||
}
|
||||
},
|
||||
"dashdash": {
|
||||
"version": "1.14.1",
|
||||
"resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
|
||||
@@ -16990,6 +17689,14 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"delaunator": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.0.0.tgz",
|
||||
"integrity": "sha512-AyLvtyJdbv/U1GkiS6gUUzclRoAY4Gs75qkMygJJhU75LW4DNuSF2RMzpxs9jw9Oz1BobHjTdkG3zdP55VxAqw==",
|
||||
"requires": {
|
||||
"robust-predicates": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"delayed-stream": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
||||
@@ -18587,6 +19294,11 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"internmap": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.1.tgz",
|
||||
"integrity": "sha512-Ujwccrj9FkGqjbY3iVoxD1VV+KdZZeENx0rphrtzmRXbFvkFO88L80BL/zeSIguX/7T+y8k04xqtgWgS5vxwxw=="
|
||||
},
|
||||
"interpret": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz",
|
||||
@@ -20235,6 +20947,11 @@
|
||||
"find-up": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"plotly.js-dist": {
|
||||
"version": "2.5.0",
|
||||
"resolved": "https://registry.npmjs.org/plotly.js-dist/-/plotly.js-dist-2.5.0.tgz",
|
||||
"integrity": "sha512-jdxnL/+yluckc6U7zFgxC4QgTvAvKriIQ0yN/gDzTd5s3YNPuCdReHB3p91Zd0fVkq4eq84I8yp/9iBbnbXLJQ=="
|
||||
},
|
||||
"pnp-webpack-plugin": {
|
||||
"version": "1.6.4",
|
||||
"resolved": "https://registry.npmjs.org/pnp-webpack-plugin/-/pnp-webpack-plugin-1.6.4.tgz",
|
||||
@@ -21406,6 +22123,11 @@
|
||||
"inherits": "^2.0.1"
|
||||
}
|
||||
},
|
||||
"robust-predicates": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.1.tgz",
|
||||
"integrity": "sha512-ndEIpszUHiG4HtDsQLeIuMvRsDnn8c8rYStabochtUeCvfuvNptb5TUbVD68LRAILPX7p9nqQGh4xJgn3EHS/g=="
|
||||
},
|
||||
"run-queue": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz",
|
||||
@@ -21415,6 +22137,11 @@
|
||||
"aproba": "^1.1.1"
|
||||
}
|
||||
},
|
||||
"rw": {
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz",
|
||||
"integrity": "sha1-P4Yt+pGrdmsUiF700BEkv9oHT7Q="
|
||||
},
|
||||
"safe-buffer": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
||||
@@ -21433,8 +22160,7 @@
|
||||
"safer-buffer": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
||||
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
|
||||
"dev": true
|
||||
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
|
||||
},
|
||||
"sass": {
|
||||
"version": "1.26.11",
|
||||
|
||||
@@ -9,12 +9,14 @@
|
||||
"dependencies": {
|
||||
"@mdi/font": "^5.6.55",
|
||||
"core-js": "^3.6.5",
|
||||
"d3": "^7.0.1",
|
||||
"jwt-decode": "^3.0.0",
|
||||
"leaflet": "^1.7.1",
|
||||
"leaflet-arrowheads": "^1.2.2",
|
||||
"leaflet-realtime": "^2.2.0",
|
||||
"leaflet.markercluster": "^1.4.1",
|
||||
"marked": "^2.0.3",
|
||||
"plotly.js-dist": "^2.5.0",
|
||||
"suncalc": "^1.8.0",
|
||||
"typeface-roboto": "0.0.75",
|
||||
"vue": "^2.6.12",
|
||||
|
||||
363
lib/www/client/source/src/components/graph-arrays-ij-scatter.vue
Normal file
363
lib/www/client/source/src/components/graph-arrays-ij-scatter.vue
Normal file
@@ -0,0 +1,363 @@
|
||||
<template>
|
||||
<v-card style="min-height:400px;">
|
||||
<v-card-title class="headline">
|
||||
Array inline / crossline error
|
||||
<v-spacer></v-spacer>
|
||||
<v-switch v-model="scatterplot" label="Scatterplot"></v-switch>
|
||||
<v-switch class="ml-4" v-model="histogram" label="Histogram"></v-switch>
|
||||
</v-card-title>
|
||||
|
||||
<v-container fluid fill-height>
|
||||
<v-row>
|
||||
<v-col>
|
||||
<div class="graph-container" ref="graph0"></div>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row v-show="scatterplot">
|
||||
<v-col>
|
||||
<div class="graph-container" ref="graph1"></div>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row v-show="histogram">
|
||||
<v-col>
|
||||
<div class="graph-container" ref="graph2"></div>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-container>
|
||||
|
||||
<v-overlay :value="busy" absolute z-index="1">
|
||||
<v-progress-circular indeterminate></v-progress-circular>
|
||||
</v-overlay>
|
||||
</v-card>
|
||||
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
.graph-container {
|
||||
background-color: red;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
<script>
|
||||
|
||||
import Plotly from 'plotly.js-dist';
|
||||
import { mapActions, mapGetters } from 'vuex';
|
||||
import unpack from '@/lib/unpack.js';
|
||||
|
||||
export default {
|
||||
name: 'DougalGraphArraysIJScatter',
|
||||
|
||||
props: [ "data", "settings" ],
|
||||
|
||||
data () {
|
||||
return {
|
||||
graph: [],
|
||||
busy: false,
|
||||
resizeObserver: null,
|
||||
scatterplot: false,
|
||||
histogram: false
|
||||
};
|
||||
},
|
||||
|
||||
computed: {
|
||||
|
||||
//...mapGetters(['apiUrl'])
|
||||
|
||||
},
|
||||
|
||||
watch: {
|
||||
|
||||
data (newVal, oldVal) {
|
||||
if (newVal === null) {
|
||||
this.busy = true;
|
||||
} else {
|
||||
this.busy = false;
|
||||
this.plot();
|
||||
}
|
||||
},
|
||||
|
||||
settings () {
|
||||
for (const key in this.settings) {
|
||||
this[key] = this.settings[key];
|
||||
}
|
||||
},
|
||||
|
||||
histogram () {
|
||||
this.plot();
|
||||
this.$emit("update:settings", {[`${this.$options.name}.histogram`]: this.histogram});
|
||||
},
|
||||
|
||||
|
||||
scatterplot () {
|
||||
this.plot();
|
||||
this.$emit("update:settings", {[`${this.$options.name}.scatterplot`]: this.scatterplot});
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
||||
plot () {
|
||||
|
||||
this.plotSeries();
|
||||
|
||||
if (this.histogram) {
|
||||
this.plotHistogram();
|
||||
}
|
||||
|
||||
if (this.scatterplot) {
|
||||
this.plotScatter();
|
||||
}
|
||||
},
|
||||
|
||||
plotSeries () {
|
||||
if (!this.data) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
function transform (d, idx=0, otherParams={}) {
|
||||
const errortype = d.errorfinal ? "errorfinal" : "errorraw";
|
||||
const coords = unpack(unpack(d, errortype), "coordinates");
|
||||
const x = unpack(d, "point");
|
||||
const y = unpack(coords, idx);
|
||||
const data = {
|
||||
type: "scatter",
|
||||
mode: "lines",
|
||||
x,
|
||||
y,
|
||||
transforms: [{
|
||||
type: "groupby",
|
||||
groups: unpack(unpack(d, "meta"), "src_number"),
|
||||
styles: [
|
||||
{target: 1, value: {line: {color: "green"}}},
|
||||
{target: 2, value: {line: {color: "red"}}}
|
||||
]
|
||||
}],
|
||||
...otherParams
|
||||
};
|
||||
return data;
|
||||
}
|
||||
|
||||
const data = [
|
||||
transform(this.data.items, 1, {
|
||||
xaxis: 'x',
|
||||
yaxis: 'y',
|
||||
name: 'Crossline'
|
||||
}),
|
||||
transform(this.data.items, 0, {
|
||||
xaxis: 'x',
|
||||
yaxis: 'y2',
|
||||
name: 'Inline'
|
||||
})
|
||||
];
|
||||
this.busy = false;
|
||||
|
||||
const layout = {
|
||||
//autosize: true,
|
||||
title: {text: "Inline / crossline error – sequence %{meta.sequence}"},
|
||||
autocolorscale: true,
|
||||
// colorscale: "sequential",
|
||||
yaxis2: {
|
||||
title: "Crossline (m)",
|
||||
anchor: "y2",
|
||||
domain: [ 0.55, 1 ]
|
||||
},
|
||||
yaxis: {
|
||||
title: "Inline (m)",
|
||||
anchor: "y1",
|
||||
domain: [ 0, 0.45 ]
|
||||
},
|
||||
xaxis: {
|
||||
title: "Shotpoint",
|
||||
anchor: "x1"
|
||||
},
|
||||
meta: this.data.meta
|
||||
};
|
||||
|
||||
const config = {
|
||||
editable: false,
|
||||
displaylogo: false
|
||||
};
|
||||
|
||||
this.graph[0] = Plotly.newPlot(this.$refs.graph0, data, layout, config);
|
||||
},
|
||||
|
||||
plotScatter () {
|
||||
|
||||
console.log("plot");
|
||||
|
||||
if (!this.data) {
|
||||
console.log("missing data");
|
||||
return;
|
||||
}
|
||||
console.log("Will plot sequence", this.data.meta.project, this.data.meta.sequence);
|
||||
|
||||
function transform (d) {
|
||||
const errortype = d.errorfinal ? "errorfinal" : "errorraw";
|
||||
const coords = unpack(unpack(d, errortype), "coordinates");
|
||||
const x = unpack(coords, 0);
|
||||
const y = unpack(coords, 1);
|
||||
const data = [{
|
||||
type: "scatter",
|
||||
mode: "markers",
|
||||
x,
|
||||
y,
|
||||
transforms: [{
|
||||
type: "groupby",
|
||||
groups: unpack(unpack(d, "meta"), "src_number"),
|
||||
styles: [
|
||||
{target: 1, value: {line: {color: "green"}}},
|
||||
{target: 2, value: {line: {color: "red"}}}
|
||||
]
|
||||
}]
|
||||
}];
|
||||
return data;
|
||||
}
|
||||
|
||||
const data = transform(this.data.items);
|
||||
this.busy = false;
|
||||
|
||||
const layout = {
|
||||
//autosize: true,
|
||||
//title: {text: "Inline / crossline error – sequence %{meta.sequence}"},
|
||||
autocolorscale: true,
|
||||
// colorscale: "sequential",
|
||||
yaxis: {
|
||||
title: "Inline (m)",
|
||||
//zeroline: false
|
||||
},
|
||||
xaxis: {
|
||||
title: "Crossline (m)"
|
||||
},
|
||||
meta: this.data.meta
|
||||
};
|
||||
|
||||
const config = {
|
||||
editable: false,
|
||||
displaylogo: false
|
||||
};
|
||||
|
||||
this.graph[1] = Plotly.newPlot(this.$refs.graph1, data, layout, config);
|
||||
},
|
||||
|
||||
plotHistogram () {
|
||||
|
||||
if (!this.data) {
|
||||
console.log("missing data");
|
||||
return;
|
||||
}
|
||||
|
||||
function transform (d, idx=0, otherParams={}) {
|
||||
const errortype = d.errorfinal ? "errorfinal" : "errorraw";
|
||||
const coords = unpack(unpack(d, errortype), "coordinates");
|
||||
const x = unpack(coords, idx);
|
||||
const data = {
|
||||
type: "histogram",
|
||||
histnorm: 'probability',
|
||||
x,
|
||||
transforms: [{
|
||||
type: "groupby",
|
||||
groups: unpack(unpack(d, "meta"), "src_number"),
|
||||
styles: [
|
||||
{target: 1, value: {marker: {color: "rgba(129, 199, 132, 0.9)"}}},
|
||||
{target: 2, value: {marker: {color: "rgba(229, 115, 115, 0.9)"}}}
|
||||
]
|
||||
}],
|
||||
...otherParams
|
||||
};
|
||||
return data;
|
||||
}
|
||||
|
||||
const data = [
|
||||
transform(this.data.items, 0, {
|
||||
xaxis: 'x',
|
||||
yaxis: 'y',
|
||||
name: 'Crossline'
|
||||
}),
|
||||
transform(this.data.items, 1, {
|
||||
xaxis: 'x2',
|
||||
yaxis: 'y',
|
||||
name: 'Inline'
|
||||
})
|
||||
];
|
||||
|
||||
const layout = {
|
||||
//autosize: true,
|
||||
//title: {text: "Inline / crossline error – sequence %{meta.sequence}"},
|
||||
legend: {
|
||||
title: { text: "Array" }
|
||||
},
|
||||
xaxis: {
|
||||
title: "Crossline distance (m)",
|
||||
domain: [ 0, 0.45 ],
|
||||
anchor: 'x1'
|
||||
},
|
||||
yaxis: {
|
||||
title: "Frequency (0‒1)",
|
||||
domain: [ 0, 1 ],
|
||||
anchor: 'y1'
|
||||
},
|
||||
xaxis2: {
|
||||
title: "Inline distance (m)",
|
||||
domain: [ 0.55, 1 ],
|
||||
anchor: 'x2'
|
||||
},
|
||||
meta: this.data.meta
|
||||
};
|
||||
|
||||
const config = {
|
||||
editable: false,
|
||||
displaylogo: false
|
||||
};
|
||||
this.busy = false;
|
||||
console.log(data);
|
||||
console.log(layout);
|
||||
|
||||
this.graph[2] = Plotly.newPlot(this.$refs.graph2, data, layout, config);
|
||||
},
|
||||
|
||||
replot () {
|
||||
if (!this.graph.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
console.log("Replotting");
|
||||
this.graph.forEach( (graph, idx) => {
|
||||
const ref = this.$refs["graph"+idx];
|
||||
Plotly.relayout(ref, {
|
||||
width: ref.clientWidth,
|
||||
height: ref.clientHeight
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
},
|
||||
|
||||
async mounted () {
|
||||
|
||||
if (this.data) {
|
||||
this.plot();
|
||||
} else {
|
||||
this.busy = true;
|
||||
}
|
||||
|
||||
this.resizeObserver = new ResizeObserver(this.replot)
|
||||
this.resizeObserver.observe(this.$refs.graph0);
|
||||
this.resizeObserver.observe(this.$refs.graph1);
|
||||
this.resizeObserver.observe(this.$refs.graph2);
|
||||
},
|
||||
|
||||
beforeDestroy () {
|
||||
if (this.resizeObserver) {
|
||||
this.resizeObserver.unobserve(this.$refs.graph2);
|
||||
this.resizeObserver.unobserve(this.$refs.graph1);
|
||||
this.resizeObserver.unobserve(this.$refs.graph0);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
</script>
|
||||
364
lib/www/client/source/src/components/graph-guns-depth.vue
Normal file
364
lib/www/client/source/src/components/graph-guns-depth.vue
Normal file
@@ -0,0 +1,364 @@
|
||||
<template>
|
||||
<v-card style="min-height:400px;">
|
||||
<v-card-title class="headline">
|
||||
Gun depth
|
||||
<v-spacer></v-spacer>
|
||||
<v-switch v-model="shotpoint" label="Shotpoint"></v-switch>
|
||||
<v-switch class="ml-4" v-model="violinplot" label="Violin plot"></v-switch>
|
||||
</v-card-title>
|
||||
|
||||
<v-container fluid fill-height>
|
||||
<v-row>
|
||||
<v-col>
|
||||
<div class="graph-container" ref="graphSeries"></div>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row v-show="shotpoint">
|
||||
<v-col>
|
||||
<div class="graph-container" ref="graphBar"></div>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row v-show="violinplot">
|
||||
<v-col>
|
||||
<div class="graph-container" ref="graphViolin"></div>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-container>
|
||||
|
||||
<v-overlay :value="busy" absolute z-index="1">
|
||||
<v-progress-circular indeterminate></v-progress-circular>
|
||||
</v-overlay>
|
||||
</v-card>
|
||||
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.graph-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
|
||||
import * as d3a from 'd3-array';
|
||||
import Plotly from 'plotly.js-dist';
|
||||
import { mapActions, mapGetters } from 'vuex';
|
||||
import unpack from '@/lib/unpack.js';
|
||||
import * as aes from '@/lib/graphs/aesthetics.js';
|
||||
|
||||
export default {
|
||||
name: 'DougalGraphGunsDepth',
|
||||
|
||||
props: [ "data", "settings" ],
|
||||
|
||||
data () {
|
||||
return {
|
||||
graph: null,
|
||||
graphHover: null,
|
||||
busy: false,
|
||||
resizeObserver: null,
|
||||
shotpoint: true,
|
||||
violinplot: false
|
||||
};
|
||||
},
|
||||
|
||||
computed: {
|
||||
//...mapGetters(['apiUrl'])
|
||||
},
|
||||
|
||||
watch: {
|
||||
|
||||
data (newVal, oldVal) {
|
||||
console.log("data changed");
|
||||
|
||||
if (newVal === null) {
|
||||
this.busy = true;
|
||||
} else {
|
||||
this.busy = false;
|
||||
this.plot();
|
||||
}
|
||||
},
|
||||
|
||||
settings () {
|
||||
for (const key in this.settings) {
|
||||
this[key] = this.settings[key];
|
||||
}
|
||||
},
|
||||
|
||||
shotpoint () {
|
||||
if (this.shotpoint) {
|
||||
this.replot();
|
||||
}
|
||||
this.$emit("update:settings", {[`${this.$options.name}.shotpoint`]: this.shotpoint});
|
||||
},
|
||||
|
||||
violinplot () {
|
||||
if (this.violinplot) {
|
||||
this.plotViolin();
|
||||
}
|
||||
this.$emit("update:settings", {[`${this.$options.name}.violinplot`]: this.violinplot});
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
||||
plot () {
|
||||
this.plotSeries();
|
||||
if (this.violinplot) {
|
||||
this.plotViolin();
|
||||
}
|
||||
},
|
||||
|
||||
async plotSeries () {
|
||||
|
||||
function transformSeries (d, src_number, otherParams={}) {
|
||||
|
||||
const meta = src_number
|
||||
? unpack(d, "meta").filter( s => s.src_number == src_number )
|
||||
: unpack(d, "meta");
|
||||
const guns = unpack(meta, "guns").map(s => s.filter(g => g[2] == src_number));;
|
||||
const gunDepths = guns.map(s => s.map(g => g[10]));
|
||||
const gunDepthsSorted = gunDepths.map(s => d3a.sort(s));
|
||||
const gunsAvgDepth = gunDepths.map( (s, sidx) => d3a.mean(s) );
|
||||
|
||||
const x = src_number
|
||||
? unpack(d.filter(s => s.meta.src_number == src_number), "point")
|
||||
: unpack(d, "point");
|
||||
|
||||
const tracesGunDepths = [{
|
||||
type: "scatter",
|
||||
mode: "lines",
|
||||
x,
|
||||
y: gunDepthsSorted.map(s => d3a.quantileSorted(s, 0.25)),
|
||||
...aes.gunArrays[src_number || 1].min
|
||||
},
|
||||
{
|
||||
type: "scatter",
|
||||
mode: "lines",
|
||||
fill: "tonexty",
|
||||
x,
|
||||
y: gunsAvgDepth,
|
||||
...aes.gunArrays[src_number || 1].avg
|
||||
},
|
||||
{
|
||||
type: "scatter",
|
||||
mode: "lines",
|
||||
fill: "tonexty",
|
||||
x,
|
||||
y: gunDepthsSorted.map(s => d3a.quantileSorted(s, 0.75)),
|
||||
...aes.gunArrays[src_number || 1].max
|
||||
}];
|
||||
|
||||
const tracesGunsDepthsIndividual = {
|
||||
//name: `Array ${src_number} outliers`,
|
||||
type: "scatter",
|
||||
mode: "markers",
|
||||
marker: {size: 2 },
|
||||
hoverinfo: "skip",
|
||||
x: gunDepthsSorted.map( (s, idx) =>
|
||||
s.filter( g => g < d3a.quantileSorted(s, 0.05) || g > d3a.quantileSorted(s, 0.95))
|
||||
.map( f => Array(f.length).fill(x[idx]) ).flat()
|
||||
).flat(),
|
||||
y: gunDepthsSorted.map( (s, idx) =>
|
||||
s.filter( g => g < d3a.quantileSorted(s, 0.05) || g > d3a.quantileSorted(s, 0.95))
|
||||
).flat(),
|
||||
...aes.gunArrays[src_number || 1].out
|
||||
};
|
||||
|
||||
const data = [ ...tracesGunDepths, tracesGunsDepthsIndividual ]
|
||||
return data;
|
||||
}
|
||||
|
||||
if (!this.data) {
|
||||
console.log("missing data");
|
||||
return;
|
||||
}
|
||||
|
||||
const sources = [ ...new Set(unpack(this.data.items, "meta").map( s => s.src_number ))];
|
||||
const data = sources.map( src_number => transformSeries(this.data.items, src_number) ).flat();
|
||||
console.log("Sources", sources);
|
||||
console.log(data);
|
||||
this.busy = false;
|
||||
|
||||
const layout = {
|
||||
//autosize: true,
|
||||
title: {text: "Gun depths – sequence %{meta.sequence}"},
|
||||
autocolorscale: true,
|
||||
// colorscale: "sequential",
|
||||
hovermode: "x",
|
||||
yaxis: {
|
||||
title: "Depth (m)",
|
||||
//zeroline: false
|
||||
},
|
||||
xaxis: {
|
||||
title: "Shotpoint",
|
||||
showspikes: true
|
||||
},
|
||||
meta: this.data.meta
|
||||
};
|
||||
|
||||
const config = {
|
||||
editable: false,
|
||||
displaylogo: false
|
||||
};
|
||||
|
||||
this.graph = Plotly.newPlot(this.$refs.graphSeries, data, layout, config);
|
||||
this.$refs.graphSeries.on('plotly_hover', (d) => {
|
||||
const point = d.points[0].x;
|
||||
const item = this.data.items.find(s => s.point == point);
|
||||
const guns = item.meta.guns.filter( g => g[2] == item.meta.src_number );
|
||||
const gunIds = guns.map( g => "G"+g[1] );
|
||||
const depths = unpack(guns, 10);
|
||||
const data = [{
|
||||
type: "bar",
|
||||
x: gunIds,
|
||||
y: depths,
|
||||
transforms: [{
|
||||
type: "groupby",
|
||||
groups: unpack(guns, 0)
|
||||
}],
|
||||
}];
|
||||
|
||||
const layout = {
|
||||
title: {text: "Gun depths – shot %{meta.point}"},
|
||||
height: 300,
|
||||
yaxis: {
|
||||
title: "Depth (m)",
|
||||
range: [ Math.min(d3a.min(depths)-0.1, 5), Math.max(d3a.max(depths)+0.1, 7) ]
|
||||
},
|
||||
xaxis: {
|
||||
title: "Gun number",
|
||||
type: 'category'
|
||||
},
|
||||
meta: {
|
||||
point
|
||||
}
|
||||
};
|
||||
|
||||
const config = { displaylogo: false };
|
||||
|
||||
Plotly.react(this.$refs.graphBar, data, layout, config);
|
||||
});
|
||||
},
|
||||
|
||||
async plotViolin () {
|
||||
|
||||
function transformViolin (d, opts = {}) {
|
||||
|
||||
const styles = [];
|
||||
|
||||
unpack(unpack(d, "meta"), "guns").flat().forEach(i => {
|
||||
const gunId = i[1];
|
||||
const arrayId = i[2];
|
||||
if (!styles[gunId]) {
|
||||
styles[gunId] = Object.assign({target: gunId}, aes.gunArrayViolins[arrayId]);
|
||||
}
|
||||
});
|
||||
|
||||
const data = {
|
||||
type: 'violin',
|
||||
x: unpack(unpack(unpack(d, "meta"), "guns").flat(), 1), // Gun number
|
||||
y: unpack(unpack(unpack(d, "meta"), "guns").flat(), 10), // Gun depth
|
||||
points: 'none',
|
||||
box: {
|
||||
visible: true
|
||||
},
|
||||
line: {
|
||||
color: 'green',
|
||||
},
|
||||
meanline: {
|
||||
visible: true
|
||||
},
|
||||
transforms: [{
|
||||
type: 'groupby',
|
||||
groups: unpack(unpack(unpack(d, "meta"), "guns").flat(), 1),
|
||||
styles: styles.filter(i => !!i)
|
||||
}]
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
|
||||
console.log("plot violin");
|
||||
if (!this.data) {
|
||||
console.log("missing data");
|
||||
return;
|
||||
}
|
||||
console.log("Will plot sequence", this.data.meta.project, this.data.meta.sequence);
|
||||
|
||||
const data = [ transformViolin(this.data.items) ];
|
||||
this.busy = false;
|
||||
|
||||
const layout = {
|
||||
//autosize: true,
|
||||
showlegend: false,
|
||||
title: {text: "Individual gun depths – sequence %{meta.sequence}"},
|
||||
autocolorscale: true,
|
||||
// colorscale: "sequential",
|
||||
yaxis: {
|
||||
title: "Depth (m)",
|
||||
zeroline: false
|
||||
},
|
||||
xaxis: {
|
||||
title: "Gun number"
|
||||
},
|
||||
meta: this.data.meta
|
||||
};
|
||||
|
||||
const config = {
|
||||
editable: false,
|
||||
displaylogo: false
|
||||
};
|
||||
|
||||
this.graph = Plotly.newPlot(this.$refs.graphViolin, data, layout, config);
|
||||
},
|
||||
|
||||
|
||||
replot () {
|
||||
if (!this.graph) {
|
||||
return;
|
||||
}
|
||||
|
||||
console.log("Replotting");
|
||||
Object.values(this.$refs).forEach( ref => {
|
||||
if (ref.data) {
|
||||
console.log("Replotting", ref, ref.clientWidth, ref.clientHeight);
|
||||
Plotly.relayout(ref, {
|
||||
width: ref.clientWidth,
|
||||
height: ref.clientHeight
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
...mapActions(["api"])
|
||||
|
||||
},
|
||||
|
||||
mounted () {
|
||||
|
||||
if (this.data) {
|
||||
this.plot();
|
||||
} else {
|
||||
this.busy = true;
|
||||
}
|
||||
|
||||
this.resizeObserver = new ResizeObserver(this.replot)
|
||||
this.resizeObserver.observe(this.$refs.graphSeries);
|
||||
this.resizeObserver.observe(this.$refs.graphViolin);
|
||||
this.resizeObserver.observe(this.$refs.graphBar);
|
||||
},
|
||||
|
||||
beforeDestroy () {
|
||||
if (this.resizeObserver) {
|
||||
this.resizeObserver.unobserve(this.$refs.graphBar);
|
||||
this.resizeObserver.unobserve(this.$refs.graphViolin);
|
||||
this.resizeObserver.unobserve(this.$refs.graphSeries);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
</script>
|
||||
405
lib/www/client/source/src/components/graph-guns-heatmap.vue
Normal file
405
lib/www/client/source/src/components/graph-guns-heatmap.vue
Normal file
@@ -0,0 +1,405 @@
|
||||
<template>
|
||||
<v-card style="min-height:400px;">
|
||||
<v-card-title class="headline">
|
||||
Gun details
|
||||
</v-card-title>
|
||||
|
||||
<v-container fluid fill-height>
|
||||
<v-row>
|
||||
<v-col>
|
||||
<div class="graph-container" ref="graphHeat"></div>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-container>
|
||||
|
||||
<v-overlay :value="busy" absolute z-index="1">
|
||||
<v-progress-circular indeterminate></v-progress-circular>
|
||||
</v-overlay>
|
||||
</v-card>
|
||||
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.graph-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
|
||||
import * as d3a from 'd3-array';
|
||||
import Plotly from 'plotly.js-dist';
|
||||
import { mapActions, mapGetters } from 'vuex';
|
||||
import unpack from '@/lib/unpack.js';
|
||||
import * as aes from '@/lib/graphs/aesthetics.js';
|
||||
|
||||
|
||||
export default {
|
||||
name: 'DougalGraphGunsDepth',
|
||||
|
||||
props: [ "data" ],
|
||||
|
||||
data () {
|
||||
return {
|
||||
graph: null,
|
||||
graphHover: null,
|
||||
busy: false,
|
||||
resizeObserver: null,
|
||||
// TODO: aspects should be a prop
|
||||
aspects: [
|
||||
"Mode", "Detect", "Autofire", "Aimpoint", "Firetime", "Delay",
|
||||
"Delta",
|
||||
"Depth", "Pressure", "Volume", "Filltime"
|
||||
]
|
||||
};
|
||||
},
|
||||
|
||||
computed: {
|
||||
//...mapGetters(['apiUrl'])
|
||||
},
|
||||
|
||||
watch: {
|
||||
|
||||
data (newVal, oldVal) {
|
||||
console.log("data changed");
|
||||
|
||||
if (newVal === null) {
|
||||
this.busy = true;
|
||||
} else {
|
||||
this.busy = false;
|
||||
this.plot();
|
||||
}
|
||||
},
|
||||
|
||||
violinplot () {
|
||||
if (this.violinplot) {
|
||||
this.plotViolin();
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
||||
plot () {
|
||||
this.plotHeat();
|
||||
},
|
||||
|
||||
async plotHeat () {
|
||||
|
||||
|
||||
if (!this.data) {
|
||||
console.log("missing data");
|
||||
return;
|
||||
}
|
||||
|
||||
function transform (data, aspects=["Depth", "Pressure"]) {
|
||||
|
||||
const facets = [
|
||||
// Mode
|
||||
{
|
||||
params: {
|
||||
name: "Mode",
|
||||
hovertemplate: "SP%{x}<br>%{y}<br>%{text}",
|
||||
},
|
||||
|
||||
text: [ "Off", "Auto", "Manual", "Disabled" ],
|
||||
|
||||
conversion: (gun, shot) => {
|
||||
switch (gun[3]) {
|
||||
case "A":
|
||||
return 1;
|
||||
case "M":
|
||||
return 2;
|
||||
case "O":
|
||||
return 0;
|
||||
case "D":
|
||||
return 3;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// Detect
|
||||
{
|
||||
params: {
|
||||
name: "Detect",
|
||||
hovertemplate: "SP%{x}<br>%{y}<br>%{text}",
|
||||
},
|
||||
|
||||
text: [ "Zero", "Peak", "Level" ],
|
||||
|
||||
conversion: (gun, shot) => {
|
||||
switch (gun[4]) {
|
||||
case "P":
|
||||
return 1;
|
||||
case "Z":
|
||||
return 0;
|
||||
case "L":
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// Autofire
|
||||
{
|
||||
params: {
|
||||
name: "Autofire",
|
||||
hovertemplate: "SP%{x}<br>%{y}<br>%{text}",
|
||||
},
|
||||
|
||||
text: [ "False", "True" ],
|
||||
|
||||
conversion: (gun, shot) => {
|
||||
return gun[5] ? 1 : 0;
|
||||
}
|
||||
},
|
||||
|
||||
// Aimpoint
|
||||
{
|
||||
params: {
|
||||
name: "Aimpoint",
|
||||
hovertemplate: "SP%{x}<br>%{y}<br>%{z} ms"
|
||||
},
|
||||
|
||||
conversion: (gun, shot) => gun[7]
|
||||
},
|
||||
|
||||
// Firetime
|
||||
{
|
||||
params: {
|
||||
name: "Firetime",
|
||||
hovertemplate: "SP%{x}<br>%{y}<br>%{z} ms"
|
||||
},
|
||||
|
||||
conversion: (gun, shot) => gun[2] == shot.meta.src_number ? gun[8] : null
|
||||
},
|
||||
|
||||
// Delta
|
||||
{
|
||||
params: {
|
||||
name: "Delta",
|
||||
hovertemplate: "SP%{x}<br>%{y}<br>%{z} ms",
|
||||
// NOTE: These values are based on
|
||||
// Grane + Snorre's ±1.5 ms spec. While a fairly
|
||||
// common range, I still consider these min / max
|
||||
// numbers to have been chosen semi-arbitrarily.
|
||||
zmin: -2,
|
||||
zmax: 2
|
||||
},
|
||||
|
||||
conversion: (gun, shot) => gun[2] == shot.meta.src_number ? gun[7]-gun[8] : null
|
||||
},
|
||||
|
||||
// Delay
|
||||
{
|
||||
params: {
|
||||
name: "Delay",
|
||||
hovertemplate: "SP%{x}<br>%{y}<br>%{z} ms"
|
||||
},
|
||||
|
||||
conversion: (gun, shot) => gun[9]
|
||||
},
|
||||
|
||||
// Depth
|
||||
{
|
||||
params: {
|
||||
name: "Depth",
|
||||
hovertemplate: "SP%{x}<br>%{y}<br>%{z} m"
|
||||
},
|
||||
|
||||
conversion: (gun, shot) => gun[10]
|
||||
},
|
||||
|
||||
// Pressure
|
||||
{
|
||||
params: {
|
||||
name: "Pressure",
|
||||
hovertemplate: "SP%{x}<br>%{y}<br>%{z} psi"
|
||||
},
|
||||
|
||||
conversion: (gun, shot) => gun[11]
|
||||
},
|
||||
|
||||
// Volume
|
||||
{
|
||||
params: {
|
||||
name: "Volume",
|
||||
hovertemplate: "SP%{x}<br>%{y}<br>%{z} in³"
|
||||
},
|
||||
|
||||
conversion: (gun, shot) => gun[12]
|
||||
},
|
||||
|
||||
// Filltime
|
||||
{
|
||||
params: {
|
||||
name: "Filltime",
|
||||
hovertemplate: "SP%{x}<br>%{y}<br>%{z} ms"
|
||||
},
|
||||
|
||||
// NOTE that filltime is applicable to the *non* firing guns
|
||||
conversion: (gun, shot) => gun[2] == shot.meta.src_number ? null : gun[13]
|
||||
}
|
||||
|
||||
|
||||
];
|
||||
|
||||
// Get gun numbers
|
||||
const guns = [...new Set(data.map( s => s.meta.guns.map( g => g[1] ) ).flat())];
|
||||
|
||||
// z eventually will have the structure:
|
||||
// z = {
|
||||
// [aspect]: [ // First shotpoint
|
||||
// [ // Value for gun 0, gun 1, … ],
|
||||
// …more shotpoints…
|
||||
// ]
|
||||
// }
|
||||
const z = {};
|
||||
|
||||
// x is an array of shotpoints
|
||||
const x = [];
|
||||
|
||||
// y is an array of gun numbers
|
||||
const y = guns.map( gun => `G${gun}` );
|
||||
|
||||
// Build array of guns (i.e., populate z)
|
||||
// We prefer to do this outside the shot-to-shot loop
|
||||
// for efficiency
|
||||
for (const facet of facets) {
|
||||
const label = facet.params.name;
|
||||
z[label] = Array(guns.length);
|
||||
for (let i=0; i<guns.length; i++) {
|
||||
z[label][i] = [];
|
||||
}
|
||||
}
|
||||
|
||||
// Populate array of guns with shotpoint data
|
||||
for (let shot of data) {
|
||||
x.push(shot.point);
|
||||
|
||||
for (const facet of facets) {
|
||||
const label = facet.params.name;
|
||||
const facetGunsArray = z[label];
|
||||
|
||||
for (const gun of shot.meta.guns) {
|
||||
const gunIndex = gun[1]-1;
|
||||
const facetGun = facetGunsArray[gunIndex];
|
||||
facetGun.push(facet.conversion(gun, shot));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return aspects.map( (aspect, idx) => {
|
||||
const facet = facets.find(el => el.params.name == aspect) || {};
|
||||
|
||||
const defaultParams = {
|
||||
name: aspect,
|
||||
type: "heatmap",
|
||||
showscale: false,
|
||||
x,
|
||||
y,
|
||||
z: z[aspect],
|
||||
text: facet.text ? z[aspect].map(row => row.map(v => facet.text[v])) : undefined,
|
||||
xaxis: "x",
|
||||
yaxis: "y" + (idx > 0 ? idx+1 : "")
|
||||
}
|
||||
|
||||
|
||||
return Object.assign({}, defaultParams, facet.params);
|
||||
});
|
||||
}
|
||||
|
||||
const data = transform(this.data.items, this.aspects);
|
||||
this.busy = false;
|
||||
|
||||
const layout = {
|
||||
title: {text: "Gun details – sequence %{meta.sequence}"},
|
||||
height: 200*this.aspects.length,
|
||||
//autocolorscale: true,
|
||||
/*
|
||||
grid: {
|
||||
rows: this.aspects.length,
|
||||
columns: 1,
|
||||
pattern: "coupled",
|
||||
roworder: "bottom to top"
|
||||
},
|
||||
*/
|
||||
//autosize: true,
|
||||
// colorscale: "sequential",
|
||||
|
||||
xaxis: {
|
||||
title: "Shotpoint",
|
||||
showspikes: true
|
||||
},
|
||||
|
||||
meta: this.data.meta
|
||||
};
|
||||
|
||||
this.aspects.forEach ( (aspect, idx) => {
|
||||
const num = idx+1;
|
||||
const key = "yaxis" + num;
|
||||
const anchor = "y" + num;
|
||||
const segment = (1/this.aspects.length);
|
||||
const margin = segment/20;
|
||||
const domain = [
|
||||
segment*idx + margin,
|
||||
segment*num - margin
|
||||
];
|
||||
layout[key] = {
|
||||
title: aspect,
|
||||
anchor,
|
||||
domain
|
||||
}
|
||||
});
|
||||
|
||||
const config = {
|
||||
//editable: true,
|
||||
displaylogo: false
|
||||
};
|
||||
|
||||
this.graph = Plotly.newPlot(this.$refs.graphHeat, data, layout, config);
|
||||
|
||||
},
|
||||
|
||||
replot () {
|
||||
if (!this.graph) {
|
||||
return;
|
||||
}
|
||||
|
||||
console.log("Replotting");
|
||||
Object.values(this.$refs).forEach( ref => {
|
||||
if (ref.data) {
|
||||
console.log("Replotting", ref, ref.clientWidth, ref.clientHeight);
|
||||
Plotly.relayout(ref, {
|
||||
width: ref.clientWidth,
|
||||
height: ref.clientHeight
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
...mapActions(["api"])
|
||||
|
||||
},
|
||||
|
||||
mounted () {
|
||||
|
||||
if (this.data) {
|
||||
this.plot();
|
||||
} else {
|
||||
this.busy = true;
|
||||
}
|
||||
|
||||
this.resizeObserver = new ResizeObserver(this.replot)
|
||||
this.resizeObserver.observe(this.$refs.graphHeat);
|
||||
},
|
||||
|
||||
beforeDestroy () {
|
||||
if (this.resizeObserver) {
|
||||
this.resizeObserver.unobserve(this.$refs.graphHeat);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
</script>
|
||||
381
lib/www/client/source/src/components/graph-guns-pressure.vue
Normal file
381
lib/www/client/source/src/components/graph-guns-pressure.vue
Normal file
@@ -0,0 +1,381 @@
|
||||
<template>
|
||||
<v-card style="min-height:400px;">
|
||||
<v-card-title class="headline">
|
||||
Gun pressures
|
||||
<v-spacer></v-spacer>
|
||||
<v-switch v-model="shotpoint" label="Shotpoint"></v-switch>
|
||||
<v-switch class="ml-4" v-model="violinplot" label="Violin plot"></v-switch>
|
||||
</v-card-title>
|
||||
|
||||
<v-container fluid fill-height>
|
||||
<v-row>
|
||||
<v-col>
|
||||
<div class="graph-container" ref="graphSeries"></div>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row v-show="shotpoint">
|
||||
<v-col>
|
||||
<div class="graph-container" ref="graphBar"></div>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row v-show="violinplot">
|
||||
<v-col>
|
||||
<div class="graph-container" ref="graphViolin"></div>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-container>
|
||||
|
||||
<v-overlay :value="busy" absolute z-index="1">
|
||||
<v-progress-circular indeterminate></v-progress-circular>
|
||||
</v-overlay>
|
||||
</v-card>
|
||||
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.graph-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
|
||||
import * as d3a from 'd3-array';
|
||||
import Plotly from 'plotly.js-dist';
|
||||
import { mapActions, mapGetters } from 'vuex';
|
||||
import unpack from '@/lib/unpack.js';
|
||||
import * as aes from '@/lib/graphs/aesthetics.js';
|
||||
|
||||
export default {
|
||||
name: 'DougalGraphGunsPressure',
|
||||
|
||||
props: [ "data", "settings" ],
|
||||
|
||||
data () {
|
||||
return {
|
||||
graph: null,
|
||||
graphHover: null,
|
||||
busy: false,
|
||||
resizeObserver: null,
|
||||
shotpoint: true,
|
||||
violinplot: false
|
||||
};
|
||||
},
|
||||
|
||||
computed: {
|
||||
//...mapGetters(['apiUrl'])
|
||||
},
|
||||
|
||||
watch: {
|
||||
|
||||
data (newVal, oldVal) {
|
||||
console.log("data changed");
|
||||
|
||||
if (newVal === null) {
|
||||
this.busy = true;
|
||||
} else {
|
||||
this.busy = false;
|
||||
this.plot();
|
||||
}
|
||||
},
|
||||
|
||||
settings () {
|
||||
for (const key in this.settings) {
|
||||
this[key] = this.settings[key];
|
||||
}
|
||||
},
|
||||
|
||||
shotpoint () {
|
||||
if (this.shotpoint) {
|
||||
this.replot();
|
||||
}
|
||||
this.$emit("update:settings", {[`${this.$options.name}.shotpoint`]: this.shotpoint});
|
||||
},
|
||||
|
||||
violinplot () {
|
||||
if (this.violinplot) {
|
||||
this.plotViolin();
|
||||
}
|
||||
this.$emit("update:settings", {[`${this.$options.name}.violinplot`]: this.violinplot});
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
||||
plot () {
|
||||
this.plotSeries();
|
||||
if (this.violinplot) {
|
||||
this.plotViolin();
|
||||
}
|
||||
},
|
||||
|
||||
async plotSeries () {
|
||||
|
||||
function transformSeries (d, src_number, otherParams={}) {
|
||||
|
||||
const meta = src_number
|
||||
? unpack(d, "meta").filter( s => s.src_number == src_number )
|
||||
: unpack(d, "meta");
|
||||
const guns = unpack(meta, "guns").map(s => s.filter(g => g[2] == src_number));;
|
||||
const gunPressures = guns.map(s => s.map(g => g[11]));
|
||||
const gunPressuresSorted = gunPressures.map(s => d3a.sort(s));
|
||||
const gunVolumes = guns.map(s => s.map(g => g[12]));
|
||||
const gunPressureWeights = gunVolumes.map( (s, sidx) => s.map( v => v/meta[sidx].volume ));
|
||||
const gunsWeightedAvgPressure = gunPressures.map( (s, sidx) =>
|
||||
d3a.sum(s.map( (pressure, gidx) => pressure * gunPressureWeights[sidx][gidx] )) / d3a.sum(gunPressureWeights[sidx])
|
||||
);
|
||||
|
||||
const manifold = unpack(meta, "manifold");
|
||||
const x = src_number
|
||||
? unpack(d.filter(s => s.meta.src_number == src_number), "point")
|
||||
: unpack(d, "point");
|
||||
|
||||
const traceManifold = {
|
||||
name: "Manifold",
|
||||
type: "scatter",
|
||||
mode: "lines",
|
||||
line: { ...aes.gunArrays[src_number || 1].avg.line, dash: "dot", width: 1 },
|
||||
x,
|
||||
y: manifold,
|
||||
};
|
||||
|
||||
const tracesGunPressures = [{
|
||||
type: "scatter",
|
||||
mode: "lines",
|
||||
x,
|
||||
y: gunPressuresSorted.map(s => d3a.quantileSorted(s, 0.25)),
|
||||
...aes.gunArrays[src_number || 1].min
|
||||
},
|
||||
{
|
||||
type: "scatter",
|
||||
mode: "lines",
|
||||
fill: "tonexty",
|
||||
x,
|
||||
y: gunsWeightedAvgPressure,
|
||||
...aes.gunArrays[src_number || 1].avg
|
||||
},
|
||||
{
|
||||
type: "scatter",
|
||||
mode: "lines",
|
||||
fill: "tonexty",
|
||||
x,
|
||||
y: gunPressuresSorted.map(s => d3a.quantileSorted(s, 0.75)),
|
||||
...aes.gunArrays[src_number || 1].max
|
||||
}];
|
||||
|
||||
const tracesGunsPressuresIndividual = {
|
||||
//name: `Array ${src_number} outliers`,
|
||||
type: "scatter",
|
||||
mode: "markers",
|
||||
marker: {size: 2 },
|
||||
hoverinfo: "skip",
|
||||
x: gunPressuresSorted.map( (s, idx) =>
|
||||
s.filter( g => g < d3a.quantileSorted(s, 0.05) || g > d3a.quantileSorted(s, 0.95))
|
||||
.map( f => Array(f.length).fill(x[idx]) ).flat()
|
||||
).flat(),
|
||||
y: gunPressuresSorted.map( (s, idx) =>
|
||||
s.filter( g => g < d3a.quantileSorted(s, 0.05) || g > d3a.quantileSorted(s, 0.95))
|
||||
).flat(),
|
||||
...aes.gunArrays[src_number || 1].out
|
||||
};
|
||||
|
||||
const data = [ traceManifold, ...tracesGunPressures, tracesGunsPressuresIndividual ]
|
||||
return data;
|
||||
}
|
||||
|
||||
if (!this.data) {
|
||||
console.log("missing data");
|
||||
return;
|
||||
}
|
||||
|
||||
const sources = [ ...new Set(unpack(this.data.items, "meta").map( s => s.src_number ))];
|
||||
const data = sources.map( src_number => transformSeries(this.data.items, src_number) ).flat();
|
||||
console.log("Sources", sources);
|
||||
console.log(data);
|
||||
this.busy = false;
|
||||
|
||||
const layout = {
|
||||
//autosize: true,
|
||||
title: {text: "Gun pressures – sequence %{meta.sequence}"},
|
||||
autocolorscale: true,
|
||||
// colorscale: "sequential",
|
||||
hovermode: "x",
|
||||
yaxis: {
|
||||
title: "Pressure (psi)",
|
||||
//zeroline: false
|
||||
},
|
||||
xaxis: {
|
||||
title: "Shotpoint",
|
||||
showspikes: true
|
||||
},
|
||||
meta: this.data.meta
|
||||
};
|
||||
|
||||
const config = {
|
||||
editable: false,
|
||||
displaylogo: false
|
||||
};
|
||||
|
||||
this.graph = Plotly.newPlot(this.$refs.graphSeries, data, layout, config);
|
||||
this.$refs.graphSeries.on('plotly_hover', (d) => {
|
||||
const point = d.points[0].x;
|
||||
const item = this.data.items.find(s => s.point == point);
|
||||
const guns = item.meta.guns.filter( g => g[2] == item.meta.src_number );
|
||||
const gunIds = guns.map( g => "G"+g[1] );
|
||||
const pressures = unpack(guns, 11);
|
||||
const volumes = unpack(guns, 12);
|
||||
const maxVolume = d3a.max(volumes);
|
||||
const data = [{
|
||||
type: "bar",
|
||||
x: gunIds,
|
||||
y: pressures,
|
||||
width: volumes.map( v => v/maxVolume ),
|
||||
transforms: [{
|
||||
type: "groupby",
|
||||
groups: unpack(guns, 0)
|
||||
}],
|
||||
}];
|
||||
|
||||
const layout = {
|
||||
title: {text: "Gun pressures – shot %{meta.point}"},
|
||||
height: 300,
|
||||
yaxis: {
|
||||
title: "Pressure (psi)",
|
||||
range: [ Math.min(d3a.min(pressures), 1950), Math.max(d3a.max(pressures), 2050) ]
|
||||
},
|
||||
xaxis: {
|
||||
title: "Gun number",
|
||||
type: 'category'
|
||||
},
|
||||
meta: {
|
||||
point
|
||||
}
|
||||
};
|
||||
|
||||
const config = { displaylogo: false };
|
||||
|
||||
Plotly.react(this.$refs.graphBar, data, layout, config);
|
||||
});
|
||||
},
|
||||
|
||||
async plotViolin () {
|
||||
|
||||
function transformViolin (d, opts = {}) {
|
||||
|
||||
const styles = [];
|
||||
|
||||
unpack(unpack(d, "meta"), "guns").flat().forEach(i => {
|
||||
const gunId = i[1];
|
||||
const arrayId = i[2];
|
||||
if (!styles[gunId]) {
|
||||
styles[gunId] = Object.assign({target: gunId}, aes.gunArrayViolins[arrayId]);
|
||||
}
|
||||
});
|
||||
|
||||
const data = {
|
||||
type: 'violin',
|
||||
x: unpack(unpack(unpack(d, "meta"), "guns").flat(), 1), // Gun number
|
||||
y: unpack(unpack(unpack(d, "meta"), "guns").flat(), 11), // Gun pressure
|
||||
points: 'none',
|
||||
box: {
|
||||
visible: true
|
||||
},
|
||||
line: {
|
||||
color: 'green',
|
||||
},
|
||||
meanline: {
|
||||
visible: true
|
||||
},
|
||||
transforms: [{
|
||||
type: 'groupby',
|
||||
groups: unpack(unpack(unpack(d, "meta"), "guns").flat(), 1),
|
||||
styles: styles.filter(i => !!i)
|
||||
}]
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
|
||||
console.log("plot violin");
|
||||
if (!this.data) {
|
||||
console.log("missing data");
|
||||
return;
|
||||
}
|
||||
console.log("Will plot sequence", this.data.meta.project, this.data.meta.sequence);
|
||||
|
||||
const data = [ transformViolin(this.data.items) ];
|
||||
this.busy = false;
|
||||
|
||||
const layout = {
|
||||
//autosize: true,
|
||||
showlegend: false,
|
||||
title: {text: "Individual gun pressures – sequence %{meta.sequence}"},
|
||||
autocolorscale: true,
|
||||
// colorscale: "sequential",
|
||||
yaxis: {
|
||||
title: "Pressure (psi)",
|
||||
zeroline: false
|
||||
},
|
||||
xaxis: {
|
||||
title: "Gun number"
|
||||
},
|
||||
meta: this.data.meta
|
||||
};
|
||||
|
||||
const config = {
|
||||
editable: false,
|
||||
displaylogo: false
|
||||
};
|
||||
|
||||
this.graph = Plotly.newPlot(this.$refs.graphViolin, data, layout, config);
|
||||
},
|
||||
|
||||
|
||||
replot () {
|
||||
if (!this.graph) {
|
||||
return;
|
||||
}
|
||||
|
||||
console.log("Replotting");
|
||||
Object.values(this.$refs).forEach( ref => {
|
||||
if (ref.data) {
|
||||
console.log("Replotting", ref, ref.clientWidth, ref.clientHeight);
|
||||
Plotly.relayout(ref, {
|
||||
width: ref.clientWidth,
|
||||
height: ref.clientHeight
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
...mapActions(["api"])
|
||||
|
||||
},
|
||||
|
||||
mounted () {
|
||||
|
||||
if (this.data) {
|
||||
this.plot();
|
||||
} else {
|
||||
this.busy = true;
|
||||
}
|
||||
|
||||
this.resizeObserver = new ResizeObserver(this.replot)
|
||||
this.resizeObserver.observe(this.$refs.graphSeries);
|
||||
this.resizeObserver.observe(this.$refs.graphViolin);
|
||||
this.resizeObserver.observe(this.$refs.graphBar);
|
||||
},
|
||||
|
||||
beforeDestroy () {
|
||||
if (this.resizeObserver) {
|
||||
this.resizeObserver.unobserve(this.$refs.graphBar);
|
||||
this.resizeObserver.unobserve(this.$refs.graphViolin);
|
||||
this.resizeObserver.unobserve(this.$refs.graphSeries);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
</script>
|
||||
364
lib/www/client/source/src/components/graph-guns-timing.vue
Normal file
364
lib/www/client/source/src/components/graph-guns-timing.vue
Normal file
@@ -0,0 +1,364 @@
|
||||
<template>
|
||||
<v-card style="min-height:400px;">
|
||||
<v-card-title class="headline">
|
||||
Gun timing
|
||||
<v-spacer></v-spacer>
|
||||
<v-switch v-model="shotpoint" label="Shotpoint"></v-switch>
|
||||
<v-switch class="ml-4" v-model="violinplot" label="Violin plot"></v-switch>
|
||||
</v-card-title>
|
||||
|
||||
<v-container fluid fill-height>
|
||||
<v-row>
|
||||
<v-col>
|
||||
<div class="graph-container" ref="graphSeries"></div>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row v-show="shotpoint">
|
||||
<v-col>
|
||||
<div class="graph-container" ref="graphBar"></div>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row v-show="violinplot">
|
||||
<v-col>
|
||||
<div class="graph-container" ref="graphViolin"></div>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-container>
|
||||
|
||||
<v-overlay :value="busy" absolute z-index="1">
|
||||
<v-progress-circular indeterminate></v-progress-circular>
|
||||
</v-overlay>
|
||||
</v-card>
|
||||
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.graph-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
|
||||
import * as d3a from 'd3-array';
|
||||
import Plotly from 'plotly.js-dist';
|
||||
import { mapActions, mapGetters } from 'vuex';
|
||||
import unpack from '@/lib/unpack.js';
|
||||
import * as aes from '@/lib/graphs/aesthetics.js';
|
||||
|
||||
export default {
|
||||
name: 'DougalGraphGunsTiming',
|
||||
|
||||
props: [ "data", "settings" ],
|
||||
|
||||
data () {
|
||||
return {
|
||||
graph: null,
|
||||
graphHover: null,
|
||||
busy: false,
|
||||
resizeObserver: null,
|
||||
shotpoint: true,
|
||||
violinplot: false
|
||||
};
|
||||
},
|
||||
|
||||
computed: {
|
||||
//...mapGetters(['apiUrl'])
|
||||
},
|
||||
|
||||
watch: {
|
||||
|
||||
data (newVal, oldVal) {
|
||||
console.log("data changed");
|
||||
|
||||
if (newVal === null) {
|
||||
this.busy = true;
|
||||
} else {
|
||||
this.busy = false;
|
||||
this.plot();
|
||||
}
|
||||
},
|
||||
|
||||
settings () {
|
||||
for (const key in this.settings) {
|
||||
this[key] = this.settings[key];
|
||||
}
|
||||
},
|
||||
|
||||
shotpoint () {
|
||||
if (this.shotpoint) {
|
||||
this.replot();
|
||||
}
|
||||
this.$emit("update:settings", {[`${this.$options.name}.shotpoint`]: this.shotpoint});
|
||||
},
|
||||
|
||||
violinplot () {
|
||||
if (this.violinplot) {
|
||||
this.plotViolin();
|
||||
}
|
||||
this.$emit("update:settings", {[`${this.$options.name}.violinplot`]: this.violinplot});
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
||||
plot () {
|
||||
this.plotSeries();
|
||||
if (this.violinplot) {
|
||||
this.plotViolin();
|
||||
}
|
||||
},
|
||||
|
||||
async plotSeries () {
|
||||
|
||||
function transformSeries (d, src_number, otherParams={}) {
|
||||
|
||||
const meta = src_number
|
||||
? unpack(d, "meta").filter( s => s.src_number == src_number )
|
||||
: unpack(d, "meta");
|
||||
const guns = unpack(meta, "guns").map(s => s.filter(g => g[2] == src_number));;
|
||||
const gunTimings = guns.map(s => s.map(g => g[9]));
|
||||
const gunTimingsSorted = gunTimings.map(s => d3a.sort(s));
|
||||
const gunsAvgTiming = gunTimings.map( (s, sidx) => d3a.mean(s) );
|
||||
|
||||
const x = src_number
|
||||
? unpack(d.filter(s => s.meta.src_number == src_number), "point")
|
||||
: unpack(d, "point");
|
||||
|
||||
const tracesGunTimings = [{
|
||||
type: "scatter",
|
||||
mode: "lines",
|
||||
x,
|
||||
y: gunTimingsSorted.map(s => d3a.quantileSorted(s, 0.25)),
|
||||
...aes.gunArrays[src_number || 1].min
|
||||
},
|
||||
{
|
||||
type: "scatter",
|
||||
mode: "lines",
|
||||
fill: "tonexty",
|
||||
x,
|
||||
y: gunsAvgTiming,
|
||||
...aes.gunArrays[src_number || 1].avg
|
||||
},
|
||||
{
|
||||
type: "scatter",
|
||||
mode: "lines",
|
||||
fill: "tonexty",
|
||||
x,
|
||||
y: gunTimingsSorted.map(s => d3a.quantileSorted(s, 0.75)),
|
||||
...aes.gunArrays[src_number || 1].max
|
||||
}];
|
||||
|
||||
const tracesGunsTimingsIndividual = {
|
||||
//name: `Array ${src_number} outliers`,
|
||||
type: "scatter",
|
||||
mode: "markers",
|
||||
marker: {size: 2 },
|
||||
hoverinfo: "skip",
|
||||
x: gunTimingsSorted.map( (s, idx) =>
|
||||
s.filter( g => g < d3a.quantileSorted(s, 0.05) || g > d3a.quantileSorted(s, 0.95))
|
||||
.map( f => Array(f.length).fill(x[idx]) ).flat()
|
||||
).flat(),
|
||||
y: gunTimingsSorted.map( (s, idx) =>
|
||||
s.filter( g => g < d3a.quantileSorted(s, 0.05) || g > d3a.quantileSorted(s, 0.95))
|
||||
).flat(),
|
||||
...aes.gunArrays[src_number || 1].out
|
||||
};
|
||||
|
||||
const data = [ ...tracesGunTimings, tracesGunsTimingsIndividual ]
|
||||
return data;
|
||||
}
|
||||
|
||||
if (!this.data) {
|
||||
console.log("missing data");
|
||||
return;
|
||||
}
|
||||
|
||||
const sources = [ ...new Set(unpack(this.data.items, "meta").map( s => s.src_number ))];
|
||||
const data = sources.map( src_number => transformSeries(this.data.items, src_number) ).flat();
|
||||
console.log("Sources", sources);
|
||||
console.log(data);
|
||||
this.busy = false;
|
||||
|
||||
const layout = {
|
||||
//autosize: true,
|
||||
title: {text: "Gun timings – sequence %{meta.sequence}"},
|
||||
autocolorscale: true,
|
||||
// colorscale: "sequential",
|
||||
hovermode: "x",
|
||||
yaxis: {
|
||||
title: "Timing (ms)",
|
||||
//zeroline: false
|
||||
},
|
||||
xaxis: {
|
||||
title: "Shotpoint",
|
||||
showspikes: true
|
||||
},
|
||||
meta: this.data.meta
|
||||
};
|
||||
|
||||
const config = {
|
||||
editable: false,
|
||||
displaylogo: false
|
||||
};
|
||||
|
||||
this.graph = Plotly.newPlot(this.$refs.graphSeries, data, layout, config);
|
||||
this.$refs.graphSeries.on('plotly_hover', (d) => {
|
||||
const point = d.points[0].x;
|
||||
const item = this.data.items.find(s => s.point == point);
|
||||
const guns = item.meta.guns.filter( g => g[2] == item.meta.src_number );
|
||||
const gunIds = guns.map( g => "G"+g[1] );
|
||||
const timings = unpack(guns, 9);
|
||||
const data = [{
|
||||
type: "bar",
|
||||
x: gunIds,
|
||||
y: timings,
|
||||
transforms: [{
|
||||
type: "groupby",
|
||||
groups: unpack(guns, 0)
|
||||
}],
|
||||
}];
|
||||
|
||||
const layout = {
|
||||
title: {text: "Gun timings – shot %{meta.point}"},
|
||||
height: 300,
|
||||
yaxis: {
|
||||
title: "Timing (ms)",
|
||||
range: [ Math.min(d3a.min(timings), 10), Math.max(d3a.max(timings), 20) ]
|
||||
},
|
||||
xaxis: {
|
||||
title: "Gun number",
|
||||
type: 'category'
|
||||
},
|
||||
meta: {
|
||||
point
|
||||
}
|
||||
};
|
||||
|
||||
const config = { displaylogo: false };
|
||||
|
||||
Plotly.react(this.$refs.graphBar, data, layout, config);
|
||||
});
|
||||
},
|
||||
|
||||
async plotViolin () {
|
||||
|
||||
function transformViolin (d, opts = {}) {
|
||||
|
||||
const styles = [];
|
||||
|
||||
unpack(unpack(d, "meta"), "guns").flat().forEach(i => {
|
||||
const gunId = i[1];
|
||||
const arrayId = i[2];
|
||||
if (!styles[gunId]) {
|
||||
styles[gunId] = Object.assign({target: gunId}, aes.gunArrayViolins[arrayId]);
|
||||
}
|
||||
});
|
||||
|
||||
const data = {
|
||||
type: 'violin',
|
||||
x: unpack(unpack(unpack(d, "meta"), "guns").flat(), 1), // Gun number
|
||||
y: unpack(unpack(unpack(d, "meta"), "guns").flat(), 9), // Gun timing
|
||||
points: 'none',
|
||||
box: {
|
||||
visible: true
|
||||
},
|
||||
line: {
|
||||
color: 'green',
|
||||
},
|
||||
meanline: {
|
||||
visible: true
|
||||
},
|
||||
transforms: [{
|
||||
type: 'groupby',
|
||||
groups: unpack(unpack(unpack(d, "meta"), "guns").flat(), 1),
|
||||
styles: styles.filter(i => !!i)
|
||||
}]
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
|
||||
console.log("plot violin");
|
||||
if (!this.data) {
|
||||
console.log("missing data");
|
||||
return;
|
||||
}
|
||||
console.log("Will plot sequence", this.data.meta.project, this.data.meta.sequence);
|
||||
|
||||
const data = [ transformViolin(this.data.items) ];
|
||||
this.busy = false;
|
||||
|
||||
const layout = {
|
||||
//autosize: true,
|
||||
showlegend: false,
|
||||
title: {text: "Individual gun timings – sequence %{meta.sequence}"},
|
||||
autocolorscale: true,
|
||||
// colorscale: "sequential",
|
||||
yaxis: {
|
||||
title: "Timing (ms)",
|
||||
zeroline: false
|
||||
},
|
||||
xaxis: {
|
||||
title: "Gun number"
|
||||
},
|
||||
meta: this.data.meta
|
||||
};
|
||||
|
||||
const config = {
|
||||
editable: false,
|
||||
displaylogo: false
|
||||
};
|
||||
|
||||
this.graph = Plotly.newPlot(this.$refs.graphViolin, data, layout, config);
|
||||
},
|
||||
|
||||
|
||||
replot () {
|
||||
if (!this.graph) {
|
||||
return;
|
||||
}
|
||||
|
||||
console.log("Replotting");
|
||||
Object.values(this.$refs).forEach( ref => {
|
||||
if (ref.data) {
|
||||
console.log("Replotting", ref, ref.clientWidth, ref.clientHeight);
|
||||
Plotly.relayout(ref, {
|
||||
width: ref.clientWidth,
|
||||
height: ref.clientHeight
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
...mapActions(["api"])
|
||||
|
||||
},
|
||||
|
||||
mounted () {
|
||||
|
||||
if (this.data) {
|
||||
this.plot();
|
||||
} else {
|
||||
this.busy = true;
|
||||
}
|
||||
|
||||
this.resizeObserver = new ResizeObserver(this.replot)
|
||||
this.resizeObserver.observe(this.$refs.graphSeries);
|
||||
this.resizeObserver.observe(this.$refs.graphViolin);
|
||||
this.resizeObserver.observe(this.$refs.graphBar);
|
||||
},
|
||||
|
||||
beforeDestroy () {
|
||||
if (this.resizeObserver) {
|
||||
this.resizeObserver.unobserve(this.$refs.graphBar);
|
||||
this.resizeObserver.unobserve(this.$refs.graphViolin);
|
||||
this.resizeObserver.unobserve(this.$refs.graphSeries);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
</script>
|
||||
145
lib/www/client/source/src/components/graph-settings-sequence.vue
Normal file
145
lib/www/client/source/src/components/graph-settings-sequence.vue
Normal file
@@ -0,0 +1,145 @@
|
||||
<template>
|
||||
|
||||
<v-dialog v-model="open">
|
||||
|
||||
<template v-slot:activator="{ on, attrs }">
|
||||
<v-btn icon v-bind="attrs" v-on="on" title="Configure visible aspects">
|
||||
<v-icon small>mdi-wrench-outline</v-icon>
|
||||
</v-btn>
|
||||
|
||||
</template>
|
||||
|
||||
<v-card>
|
||||
<v-list nav subheader>
|
||||
|
||||
<v-subheader>Visualisations</v-subheader>
|
||||
|
||||
<v-list-item-group v-model="aspectsVisible" multiple>
|
||||
|
||||
<v-list-item value="DougalGraphGunsPressure">
|
||||
<template v-slot:default="{ active }">
|
||||
<v-list-item-action>
|
||||
<v-checkbox :input-value="active"></v-checkbox>
|
||||
</v-list-item-action>
|
||||
|
||||
<v-list-item-content>
|
||||
<v-list-item-title>Series: Gun pressure</v-list-item-title>
|
||||
<v-list-item-subtitle>Array pressures weighted averages</v-list-item-subtitle>
|
||||
</v-list-item-content>
|
||||
</template>
|
||||
</v-list-item>
|
||||
|
||||
<v-list-item value="DougalGraphGunsTiming">
|
||||
<template v-slot:default="{ active }">
|
||||
<v-list-item-action>
|
||||
<v-checkbox :input-value="active"></v-checkbox>
|
||||
</v-list-item-action>
|
||||
|
||||
<v-list-item-content>
|
||||
<v-list-item-title>Series: Gun timing</v-list-item-title>
|
||||
<v-list-item-subtitle>Array timing averages</v-list-item-subtitle>
|
||||
</v-list-item-content>
|
||||
</template>
|
||||
</v-list-item>
|
||||
|
||||
<v-list-item value="DougalGraphGunsDepth">
|
||||
<template v-slot:default="{ active }">
|
||||
<v-list-item-action>
|
||||
<v-checkbox :input-value="active"></v-checkbox>
|
||||
</v-list-item-action>
|
||||
|
||||
<v-list-item-content>
|
||||
<v-list-item-title>Series: Gun depth</v-list-item-title>
|
||||
<v-list-item-subtitle>Array depths averages</v-list-item-subtitle>
|
||||
</v-list-item-content>
|
||||
</template>
|
||||
</v-list-item>
|
||||
|
||||
<v-list-item value="DougalGraphGunsHeatmap">
|
||||
<template v-slot:default="{ active }">
|
||||
<v-list-item-action>
|
||||
<v-checkbox :input-value="active"></v-checkbox>
|
||||
</v-list-item-action>
|
||||
|
||||
<v-list-item-content>
|
||||
<v-list-item-title>Heatmap: Gun parameters</v-list-item-title>
|
||||
<v-list-item-subtitle>Detail of every gun × every shotpoint</v-list-item-subtitle>
|
||||
</v-list-item-content>
|
||||
</template>
|
||||
</v-list-item>
|
||||
|
||||
<v-list-item value="DougalGraphArraysIJScatter">
|
||||
<template v-slot:default="{ active }">
|
||||
<v-list-item-action>
|
||||
<v-checkbox :input-value="active"></v-checkbox>
|
||||
</v-list-item-action>
|
||||
|
||||
<v-list-item-content>
|
||||
<v-list-item-title>Series: I/J error</v-list-item-title>
|
||||
<v-list-item-subtitle>Inline / crossline error</v-list-item-subtitle>
|
||||
</v-list-item-content>
|
||||
</template>
|
||||
</v-list-item>
|
||||
|
||||
</v-list-item-group>
|
||||
</v-list>
|
||||
|
||||
<v-divider></v-divider>
|
||||
<v-card-actions>
|
||||
<v-btn v-if="user" color="warning" text @click="save" :title="'Save as preference for user '+user.name+' on this computer (other users may have other defaults).'">Save as default</v-btn>
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn color="primary" text @click="open=false">Close</v-btn>
|
||||
</v-card-actions>
|
||||
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
import { mapActions, mapGetters } from 'vuex';
|
||||
|
||||
export default {
|
||||
name: "DougalGraphSettingsSequence",
|
||||
|
||||
props: [
|
||||
"aspects"
|
||||
],
|
||||
|
||||
data () {
|
||||
return {
|
||||
open: false,
|
||||
aspectsVisible: this.aspects || []
|
||||
}
|
||||
},
|
||||
|
||||
watch: {
|
||||
|
||||
aspects () {
|
||||
// Update the aspects selection list iff the list
|
||||
// is not currently open.
|
||||
if (!this.open) {
|
||||
this.aspectsVisible = this.aspects;
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
computed: {
|
||||
...mapGetters(['user', 'writeaccess', 'loading', 'serverEvent'])
|
||||
},
|
||||
|
||||
methods: {
|
||||
save () {
|
||||
this.open = false;
|
||||
this.$nextTick( () => this.$emit("update:aspects", {aspects: [...this.aspectsVisible]}) );
|
||||
},
|
||||
|
||||
reset () {
|
||||
this.aspectsVisible = this.aspects || [];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
@@ -103,6 +103,7 @@ export default {
|
||||
{ href: "calendar", text: "Calendar" },
|
||||
{ href: "log", text: "Log" },
|
||||
{ href: "qc", text: "QC" },
|
||||
{ href: "graphs", text: "Graphs" },
|
||||
{ href: "map", text: "Map" }
|
||||
],
|
||||
path: []
|
||||
|
||||
88
lib/www/client/source/src/lib/graphs/aesthetics.js
Normal file
88
lib/www/client/source/src/lib/graphs/aesthetics.js
Normal file
@@ -0,0 +1,88 @@
|
||||
export const gunArrays = {
|
||||
1: {
|
||||
min: {
|
||||
fillcolor: "rgba(200, 230, 201, 0.2)",
|
||||
line: {color: "rgba(129, 199, 132, 0.3)", shape: "spline"},
|
||||
showlegend: false,
|
||||
name: "Array 1 (min.)",
|
||||
hoverinfo: "skip"
|
||||
},
|
||||
avg: {
|
||||
fillcolor: "rgba(200, 230, 201, 0.2)",
|
||||
line: {color: "rgba(129, 199, 132, 0.9)", shape: "spline"},
|
||||
name: "Array 1 (avg.)"
|
||||
},
|
||||
max: {
|
||||
fillcolor: "rgba(200, 230, 201, 0.2)",
|
||||
line: {color: "rgba(129, 199, 132, 0.4)", shape: "spline"},
|
||||
showlegend: false,
|
||||
name: "Array 1 (max.)",
|
||||
hoverinfo: "skip"
|
||||
},
|
||||
out: {
|
||||
name: "Array 1 outliers",
|
||||
line: {color: "rgba(129, 199, 166, 0.7)"},
|
||||
fillcolor: "rgba(129, 199, 166, 0.5)"
|
||||
}
|
||||
},
|
||||
2: {
|
||||
min: {
|
||||
fillcolor: "rgba(255, 205, 210, 0.2)",
|
||||
line: {color: "rgba(229, 115, 115, 0.3)", shape: "spline"},
|
||||
showlegend: false,
|
||||
name: "Array 2 (min.)",
|
||||
hoverinfo: "skip"
|
||||
},
|
||||
avg: {
|
||||
fillcolor: "rgba(255, 205, 210, 0.2)",
|
||||
line: {color: "rgba(229, 115, 115, 0.9)", shape: "spline"},
|
||||
name: "Array 2 (avg.)"
|
||||
},
|
||||
max: {
|
||||
fillcolor: "rgba(255, 205, 210, 0.2)",
|
||||
line: {color: "rgba(229, 115, 115, 0.4)", shape: "spline"},
|
||||
showlegend: false,
|
||||
name: "Array 2 (max.)",
|
||||
hoverinfo: "skip"
|
||||
},
|
||||
out: {
|
||||
name: "Array 2 outliers",
|
||||
line: {color: "rgba(229, 153, 115, 0.7)"},
|
||||
fillcolor: "rgba(229, 153, 115, 0.5)"
|
||||
}
|
||||
},
|
||||
3: {
|
||||
min: {
|
||||
fillcolor: "",
|
||||
line: {color: "", shape: "spline"},
|
||||
showlegend: false,
|
||||
name: "Array 3 (min.)",
|
||||
hoverinfo: "skip"
|
||||
},
|
||||
avg: {
|
||||
fillcolor: "",
|
||||
line: {color: "", shape: "spline"},
|
||||
name: "Array 3 (avg.)"
|
||||
},
|
||||
max: {
|
||||
fillcolor: "",
|
||||
line: {color: "", shape: "spline"},
|
||||
showlegend: false,
|
||||
name: "Array 3 (max.)",
|
||||
hoverinfo: "skip"
|
||||
},
|
||||
out: {
|
||||
name: "Array 3 outliers",
|
||||
//fillcolor: ""
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const gunArrayViolins = {
|
||||
1: {
|
||||
value: {line: {color: "rgba(129, 199, 132, 0.9)"}}
|
||||
},
|
||||
2: {
|
||||
value: {line: {color: "rgba(229, 115, 115, 0.9)"}}
|
||||
}
|
||||
};
|
||||
4
lib/www/client/source/src/lib/unpack.js
Normal file
4
lib/www/client/source/src/lib/unpack.js
Normal file
@@ -0,0 +1,4 @@
|
||||
|
||||
export default function unpack(rows, key) {
|
||||
return rows && rows.map( row => row[key] );
|
||||
};
|
||||
@@ -92,4 +92,50 @@ function geometryAsString (item, opts = {}) {
|
||||
return str;
|
||||
}
|
||||
|
||||
export { withParentProps, geometryAsString }
|
||||
/** Extract preferences by prefix.
|
||||
*
|
||||
* This function returns a lambda which, given
|
||||
* a key or a prefix, extracts the relevant
|
||||
* preferences from the designated preferences
|
||||
* store.
|
||||
*
|
||||
* For instance, assume preferences = {
|
||||
* "a.b.c.d": 1,
|
||||
* "a.b.e.f": 2,
|
||||
* "g.h": 3
|
||||
* }
|
||||
*
|
||||
* And λ = preferencesλ(preferences). Then:
|
||||
*
|
||||
* λ("a.b") → { "a.b.c.d": 1, "a.b.e.f": 2 }
|
||||
* λ("a.b.e.f") → { "a.b.e.f": 2 }
|
||||
* λ("g.x", {"g.x.": 99}) → { "g.x.": 99 }
|
||||
* λ("a.c", {"g.x.": 99}) → { "g.x.": 99 }
|
||||
*
|
||||
* Note from the last two examples that a default value
|
||||
* may be provided and will be returned if a key does
|
||||
* not exist or is not searched for.
|
||||
*/
|
||||
function preferencesλ (preferences) {
|
||||
|
||||
return function (key, defaults={}) {
|
||||
const keys = Object.keys(preferences).filter(str => str.startsWith(key+".") || str == key);
|
||||
|
||||
const settings = {...defaults};
|
||||
for (const str of keys) {
|
||||
const k = str == key ? str : str.substring(key.length+1);
|
||||
const v = preferences[str];
|
||||
settings[k] = v;
|
||||
}
|
||||
|
||||
return settings;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
export {
|
||||
withParentProps,
|
||||
geometryAsString,
|
||||
preferencesλ
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ import SequenceSummary from '../views/SequenceSummary.vue'
|
||||
import Calendar from '../views/Calendar.vue'
|
||||
import Log from '../views/Log.vue'
|
||||
import QC from '../views/QC.vue'
|
||||
import Graphs from '../views/Graphs.vue'
|
||||
import Map from '../views/Map.vue'
|
||||
|
||||
|
||||
@@ -150,6 +151,16 @@ Vue.use(VueRouter)
|
||||
path: "qc",
|
||||
component: QC
|
||||
},
|
||||
{
|
||||
path: "graphs",
|
||||
component: Graphs,
|
||||
children: [
|
||||
{ path: "sequence/:sequence", name: "graphsBySequence" },
|
||||
{ path: "sequence/:sequence0/:sequence1", name: "graphsBySequences" },
|
||||
{ path: "date/:date0", name: "graphsByDate" },
|
||||
{ path: "date/:date0/:date1", name: "graphsByDates" }
|
||||
]
|
||||
},
|
||||
{
|
||||
path: "map",
|
||||
name: "map",
|
||||
|
||||
@@ -11,15 +11,19 @@ async function login ({commit, dispatch}, loginRequest) {
|
||||
}
|
||||
const res = await dispatch('api', [url, init]);
|
||||
if (res && res.ok) {
|
||||
dispatch('setCredentials', true);
|
||||
await dispatch('setCredentials', true);
|
||||
await dispatch('loadUserPreferences');
|
||||
}
|
||||
}
|
||||
|
||||
async function logout ({commit, dispatch}) {
|
||||
commit('setCookie', null);
|
||||
commit('setUser', null);
|
||||
await dispatch('api', ["/logout"]);
|
||||
// Should delete JWT cookie
|
||||
await dispatch('api', ["/logout"]);
|
||||
|
||||
// Clear preferences
|
||||
commit('setPreferences', {});
|
||||
}
|
||||
|
||||
function browserCookie (state) {
|
||||
@@ -45,6 +49,56 @@ function setCredentials ({state, commit, getters, dispatch}, force = false) {
|
||||
}
|
||||
}
|
||||
}
|
||||
dispatch('loadUserPreferences');
|
||||
}
|
||||
|
||||
export default { login, logout, setCredentials };
|
||||
/**
|
||||
* Save user preferences to localStorage and store.
|
||||
*
|
||||
* User preferences are identified by a key that gets
|
||||
* prefixed with the user name and role. The value can
|
||||
* be anything that JSON.stringify can parse.
|
||||
*/
|
||||
function saveUserPreference ({state, commit}, [key, value]) {
|
||||
const k = `${state.user?.name}.${state.user?.role}.${key}`;
|
||||
|
||||
if (value !== undefined) {
|
||||
localStorage.setItem(k, JSON.stringify(value));
|
||||
|
||||
const preferences = state.preferences;
|
||||
preferences[key] = value;
|
||||
commit('setPreferences', preferences);
|
||||
} else {
|
||||
localStorage.removeItem(k);
|
||||
|
||||
const preferences = state.preferences;
|
||||
delete preferences[key];
|
||||
commit('setPreferences', preferences);
|
||||
}
|
||||
}
|
||||
|
||||
async function loadUserPreferences ({state, commit}) {
|
||||
// Get all keys which are of interest to us
|
||||
const prefix = `${state.user?.name}.${state.user?.role}`;
|
||||
const keys = Object.keys(localStorage).filter( k => k.startsWith(prefix) );
|
||||
|
||||
// Build the preferences object
|
||||
const preferences = {};
|
||||
keys.map(str => {
|
||||
const value = JSON.parse(localStorage.getItem(str));
|
||||
const key = str.split(".").slice(2).join(".");
|
||||
preferences[key] = value;
|
||||
});
|
||||
|
||||
// Commit it
|
||||
commit('setPreferences', preferences);
|
||||
}
|
||||
|
||||
|
||||
export default {
|
||||
login,
|
||||
logout,
|
||||
setCredentials,
|
||||
saveUserPreference,
|
||||
loadUserPreferences
|
||||
};
|
||||
|
||||
@@ -11,4 +11,8 @@ function adminaccess (state) {
|
||||
return state.user && state.user.role == "admin";
|
||||
}
|
||||
|
||||
export default { user, writeaccess, adminaccess };
|
||||
function preferences (state) {
|
||||
return state.preferences;
|
||||
}
|
||||
|
||||
export default { user, writeaccess, adminaccess, preferences };
|
||||
|
||||
@@ -7,4 +7,8 @@ function setUser (state, user) {
|
||||
state.user = user;
|
||||
}
|
||||
|
||||
export default { setCookie, setUser };
|
||||
function setPreferences (state, preferences) {
|
||||
state.preferences = preferences;
|
||||
}
|
||||
|
||||
export default { setCookie, setUser, setPreferences };
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
const state = () => ({
|
||||
cookie: null,
|
||||
user: null,
|
||||
preferences: {}
|
||||
});
|
||||
|
||||
export default state;
|
||||
|
||||
331
lib/www/client/source/src/views/Graphs.vue
Normal file
331
lib/www/client/source/src/views/Graphs.vue
Normal file
@@ -0,0 +1,331 @@
|
||||
<template>
|
||||
<v-card>
|
||||
|
||||
<v-toolbar v-if="$route.params.sequence" class="fixed">
|
||||
|
||||
<v-toolbar-title>
|
||||
Sequence {{$route.params.sequence}}
|
||||
</v-toolbar-title>
|
||||
|
||||
<v-spacer></v-spacer>
|
||||
|
||||
<dougal-graph-settings-sequence :aspects="aspects" @update:aspects="configure">
|
||||
</dougal-graph-settings-sequence>
|
||||
|
||||
<v-btn icon
|
||||
:disabled="!($route.params.sequence > firstSequence)"
|
||||
:to="{name: 'graphsBySequence', params: { sequence: firstSequence }}"
|
||||
title="Go to the first sequence"
|
||||
>
|
||||
<v-icon>mdi-skip-backward</v-icon>
|
||||
</v-btn>
|
||||
<v-btn icon
|
||||
:disabled="!prevSequence"
|
||||
:to="{name: 'graphsBySequence', params: { sequence: prevSequence }}"
|
||||
title="Go to the previous sequence"
|
||||
>
|
||||
<v-icon>mdi-skip-previous</v-icon>
|
||||
</v-btn>
|
||||
<v-menu
|
||||
:close-on-content-click="false"
|
||||
:disabled="!sequences.length"
|
||||
>
|
||||
<template v-slot:activator="{ on, attrs }">
|
||||
<v-btn icon v-bind="attrs" v-on="on" :disabled="!sequences.length" title="Jump to sequence…">
|
||||
<v-icon>mdi-debug-step-over</v-icon>
|
||||
</v-btn>
|
||||
</template>
|
||||
|
||||
<v-list>
|
||||
<v-list-item>
|
||||
<v-autocomplete
|
||||
:value="$route.params.sequence*1"
|
||||
:items="sequences"
|
||||
item-text="sequence"
|
||||
item-value="sequence"
|
||||
@change="(sequence) => $router.push({name: 'graphsBySequence', params: {sequence}})"
|
||||
>
|
||||
</v-autocomplete>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-menu>
|
||||
<v-btn icon
|
||||
:disabled="!nextSequence"
|
||||
:to="{name: 'graphsBySequence', params: { sequence: nextSequence }}"
|
||||
title="Go to the next sequence"
|
||||
>
|
||||
<v-icon>mdi-skip-next</v-icon>
|
||||
</v-btn>
|
||||
<v-btn icon
|
||||
:disabled="!($route.params.sequence < lastSequence)"
|
||||
:to="{name: 'graphsBySequence', params: { sequence: lastSequence }}"
|
||||
title="Go to the last sequence"
|
||||
>
|
||||
<v-icon>mdi-skip-forward</v-icon>
|
||||
</v-btn>
|
||||
|
||||
</v-toolbar>
|
||||
|
||||
<v-toolbar v-else-if="$route.params.sequence0">
|
||||
|
||||
<v-toolbar-title>
|
||||
Sequences {{$route.params.sequence0}}‒{{$route.params.sequence1}}
|
||||
</v-toolbar-title>
|
||||
|
||||
</v-toolbar>
|
||||
|
||||
<v-toolbar v-else-if="$route.params.date">
|
||||
|
||||
<v-toolbar-title>
|
||||
Date {{$route.params.date}}
|
||||
</v-toolbar-title>
|
||||
|
||||
</v-toolbar>
|
||||
|
||||
<v-toolbar v-else-if="$route.params.date0">
|
||||
|
||||
<v-toolbar-title>
|
||||
Dates {{$route.params.date0}}‒{{$route.params.date1}}
|
||||
</v-toolbar-title>
|
||||
|
||||
</v-toolbar>
|
||||
|
||||
|
||||
<v-toolbar flat>
|
||||
<!--
|
||||
This is a “ghost” toolbar so that elements further down in the page are
|
||||
not hidden behind the (now position: fixed) real toolbar.
|
||||
-->
|
||||
</v-toolbar>
|
||||
|
||||
<v-container>
|
||||
<v-row v-for="(item, idx) in visibleItems" :key="idx">
|
||||
<v-col>
|
||||
<component
|
||||
:is="item.component"
|
||||
:data="attributesFor(item)"
|
||||
:settings="preferencesFor(item.component)"
|
||||
@update:settings="configure">
|
||||
</component>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-container>
|
||||
</v-card>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
.v-toolbar.fixed {
|
||||
position: fixed;
|
||||
width: 100%;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.empty-cell {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
border: 1px dashed lightgray;
|
||||
border-radius: 4px;
|
||||
padding: 4px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
|
||||
import { mapActions, mapGetters } from 'vuex';
|
||||
import { preferencesλ } from '@/lib/utils.js';
|
||||
import DougalGraphGunsPressure from '@/components/graph-guns-pressure.vue';
|
||||
import DougalGraphGunsTiming from '@/components/graph-guns-timing.vue';
|
||||
import DougalGraphGunsDepth from '@/components/graph-guns-depth.vue';
|
||||
import DougalGraphGunsHeatmap from '@/components/graph-guns-heatmap.vue';
|
||||
import DougalGraphArraysIJScatter from '@/components/graph-arrays-ij-scatter.vue';
|
||||
import DougalGraphSettingsSequence from '@/components/graph-settings-sequence.vue';
|
||||
|
||||
export default {
|
||||
name: "Graphs",
|
||||
|
||||
components: {
|
||||
DougalGraphSettingsSequence,
|
||||
DougalGraphArraysIJScatter,
|
||||
DougalGraphGunsPressure,
|
||||
DougalGraphGunsTiming,
|
||||
DougalGraphGunsDepth,
|
||||
DougalGraphGunsHeatmap
|
||||
},
|
||||
|
||||
data () {
|
||||
const items = [
|
||||
{
|
||||
component: "DougalGraphGunsPressure",
|
||||
},
|
||||
{
|
||||
component: "DougalGraphGunsTiming",
|
||||
},
|
||||
{
|
||||
component: "DougalGraphGunsDepth",
|
||||
},
|
||||
{
|
||||
component: "DougalGraphGunsHeatmap",
|
||||
},
|
||||
{
|
||||
component: "DougalGraphArraysIJScatter",
|
||||
attributes: {
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
return {
|
||||
items,
|
||||
data: null,
|
||||
sequences: [],
|
||||
jumpToSequence: null,
|
||||
aspects: items.map(i => i.component)
|
||||
};
|
||||
},
|
||||
|
||||
watch: {
|
||||
preferences () {
|
||||
this.configure(preferencesλ(this.preferences)(this.$options.name, {aspects: this.aspects}))
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
|
||||
getRows() {
|
||||
return Array(this.rows).fill().map( (el, idx) => idx );
|
||||
},
|
||||
|
||||
getCols () {
|
||||
return Array(this.cols).fill().map( (el, idx) => idx );
|
||||
},
|
||||
|
||||
visibleItems () {
|
||||
return this.items.filter( i => this.aspects.includes(i.component) );
|
||||
},
|
||||
|
||||
firstSequence () {
|
||||
return this.sequences[this.sequences.length-1]?.sequence;
|
||||
},
|
||||
|
||||
prevSequence () {
|
||||
const seq = Number(this.$route.params.sequence);
|
||||
const val = this.sequences
|
||||
.filter(i => i.sequence < seq)
|
||||
.map(i => i.sequence)
|
||||
.reduce( (acc, cur) => Math.max(acc, cur), -Infinity);
|
||||
|
||||
return isFinite(val) ? val : undefined;
|
||||
},
|
||||
|
||||
nextSequence () {
|
||||
const seq = Number(this.$route.params.sequence);
|
||||
const val = this.sequences
|
||||
.filter(i => i.sequence > seq)
|
||||
.map(i => i.sequence)
|
||||
.reduce( (acc, cur) => Math.min(acc, cur), +Infinity);
|
||||
|
||||
return isFinite(val) ? val : undefined;
|
||||
},
|
||||
|
||||
lastSequence () {
|
||||
return this.sequences[0]?.sequence;
|
||||
},
|
||||
|
||||
...mapGetters(['user', 'preferences', 'writeaccess', 'loading', 'serverEvent'])
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
||||
configure (data) {
|
||||
if ("aspects" in data) {
|
||||
this.aspects = [...data.aspects];
|
||||
}
|
||||
for (const key in data) {
|
||||
this.saveUserPreference([`${this.$options.name}.${key}`, data[key]]);
|
||||
}
|
||||
},
|
||||
|
||||
attributesFor (item) {
|
||||
return this.data
|
||||
? Object.assign({
|
||||
items: this.data,
|
||||
meta: {...this.$route.params}
|
||||
}, item?.attributes)
|
||||
: null;
|
||||
},
|
||||
|
||||
preferencesFor (key, defaults) {
|
||||
return preferencesλ(this.preferences)(`${this.$options.name}.${key}`, defaults);
|
||||
},
|
||||
|
||||
gotoSequence(seq) {
|
||||
this.$route.params.sequence = seq;
|
||||
},
|
||||
|
||||
...mapActions(["api", "showSnack", "saveUserPreference"])
|
||||
},
|
||||
|
||||
beforeRouteLeave (to, from, next) {
|
||||
this.data = null;
|
||||
console.log("beforeRouteLeave");
|
||||
next();
|
||||
},
|
||||
|
||||
async beforeRouteUpdate (to, from, next) {
|
||||
console.log("beforeRouteUpdate");
|
||||
this.data = null;
|
||||
next();
|
||||
|
||||
const url = `/project/${this.$route.params.project}/sequence/${this.$route.params.sequence}?project=sequence,point,tstamp,geometrypreplot,errorraw,errorfinal,meta&path=$.raw.smsrc`;
|
||||
this.data = Object.freeze(await this.api([url]));
|
||||
this.sequences = await this.api([`/project/${this.$route.params.project}/sequence`]);
|
||||
},
|
||||
|
||||
async beforeRouteEnter (to, from, next) {
|
||||
console.log("beforeRouteEnter enter");
|
||||
|
||||
next( async vm => {
|
||||
if (vm.$route.params.sequence) {
|
||||
const url = `/project/${vm.$route.params.project}/sequence/${vm.$route.params.sequence}?project=sequence,point,tstamp,geometrypreplot,errorraw,errorfinal,meta&path=$.raw.smsrc`;
|
||||
|
||||
vm.data = null;
|
||||
vm.api([url]).then( d => vm.data = Object.freeze(d) );
|
||||
vm.api([`/project/${vm.$route.params.project}/sequence`]).then( d => vm.sequences = d );
|
||||
} else {
|
||||
// FIXME Ultra-dirty hack to get a result when navigating directly to ‘Graphs’
|
||||
if (!vm.sequences.length) {
|
||||
vm.sequences = await vm.api([`/project/${vm.$route.params.project}/sequence`]);
|
||||
}
|
||||
vm.$router.push({name: "graphsBySequence", params: {
|
||||
project: vm.$route.params.project,
|
||||
sequence: vm.sequences[0]?.sequence
|
||||
}});
|
||||
}
|
||||
|
||||
console.log("beforeRouteEnter exit");
|
||||
});
|
||||
},
|
||||
|
||||
async mounted () {
|
||||
console.log("Graphs mounted");
|
||||
this.sequences = await this.api([`/project/${this.$route.params.project}/sequence`]);
|
||||
|
||||
if (!this.$route.params.sequence) {
|
||||
this.$router.push({name: "graphsBySequence", params: {
|
||||
project: this.$route.params.project,
|
||||
sequence: this.sequences[0]?.sequence
|
||||
}});
|
||||
}
|
||||
|
||||
const url = `/project/${this.$route.params.project}/sequence/${this.$route.params.sequence}?project=sequence,point,tstamp,geometrypreplot,errorraw,errorfinal,meta&path=$.raw.smsrc`;
|
||||
|
||||
this.data = Object.freeze(await this.api([url]));
|
||||
console.log("Mount finished");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
</script>
|
||||
@@ -34,6 +34,12 @@
|
||||
<v-list-item @click="addToPlan(true); contextMenuShow=false" v-if="writeaccess">
|
||||
<v-list-item-title>Reshoot with overlap</v-list-item-title>
|
||||
</v-list-item>
|
||||
<v-list-item
|
||||
:href="`/projects/${$route.params.project}/graphs/sequence/${contextMenuItem.sequence}`"
|
||||
@click="contextMenuShow=false"
|
||||
>
|
||||
<v-list-item-title>View graphics</v-list-item-title>
|
||||
</v-list-item>
|
||||
<v-list-group>
|
||||
<template v-slot:activator>
|
||||
<v-list-item-title>Download report</v-list-item-title>
|
||||
|
||||
@@ -117,7 +117,7 @@ app.map({
|
||||
get: [ mw.sequence.list ],
|
||||
},
|
||||
'/project/:project/sequence/:sequence': {
|
||||
// get: [ mw.sequence.get ],
|
||||
get: [ mw.sequence.get ],
|
||||
patch: [ mw.auth.access.write, mw.sequence.patch ],
|
||||
},
|
||||
|
||||
|
||||
28
lib/www/server/api/middleware/sequence/get/geojson.js
Normal file
28
lib/www/server/api/middleware/sequence/get/geojson.js
Normal file
@@ -0,0 +1,28 @@
|
||||
|
||||
const { sequence } = require('../../../../lib/db');
|
||||
|
||||
module.exports = async function (req, res, next) {
|
||||
|
||||
try {
|
||||
const json = await sequence.get(req.params.project, req.params.sequence, req.query);
|
||||
const geometry = req.query.geometry || "geometrypreplot";
|
||||
|
||||
const geojson = {
|
||||
type: "FeatureCollection",
|
||||
features: json.map(feature => {
|
||||
return {
|
||||
type: "Feature",
|
||||
geometry: feature[geometry],
|
||||
properties: {...feature}
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
res.status(200).send(geojson);
|
||||
next();
|
||||
} catch (err) {
|
||||
next(err);
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
23
lib/www/server/api/middleware/sequence/get/index.js
Normal file
23
lib/www/server/api/middleware/sequence/get/index.js
Normal file
@@ -0,0 +1,23 @@
|
||||
const json = require('./json');
|
||||
const geojson = require('./geojson');
|
||||
|
||||
module.exports = async function (req, res, next) {
|
||||
try {
|
||||
const handlers = {
|
||||
"application/json": json,
|
||||
"application/geo+json": geojson,
|
||||
};
|
||||
|
||||
const mimetype = (handlers[req.query.mime] && req.query.mime) || req.accepts(Object.keys(handlers));
|
||||
|
||||
if (mimetype) {
|
||||
res.set("Content-Type", mimetype);
|
||||
await handlers[mimetype](req, res, next);
|
||||
} else {
|
||||
res.status(406).send();
|
||||
next();
|
||||
}
|
||||
} catch (err) {
|
||||
next(err);
|
||||
}
|
||||
}
|
||||
14
lib/www/server/api/middleware/sequence/get/json.js
Normal file
14
lib/www/server/api/middleware/sequence/get/json.js
Normal file
@@ -0,0 +1,14 @@
|
||||
|
||||
const { sequence } = require('../../../../lib/db');
|
||||
|
||||
module.exports = async function (req, res, next) {
|
||||
|
||||
try {
|
||||
res.status(200).send(await sequence.get(req.params.project, req.params.sequence, req.query));
|
||||
next();
|
||||
} catch (err) {
|
||||
next(err);
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
@@ -0,0 +1,92 @@
|
||||
const { setSurvey } = require('../connection');
|
||||
|
||||
function thinout (key, obj) {
|
||||
const path = key.split(".");
|
||||
// console.log("path", path);
|
||||
const value = path.reduce( (a, b, i) => {
|
||||
// console.log("index", i);
|
||||
if (a !== null && typeof a != "undefined") {
|
||||
if (b == "*" && Array.isArray(a)) {
|
||||
const subkey = path.splice(i+1).join(".");
|
||||
console.log("subkey", subkey);
|
||||
return a.map(e => thinout(subkey, e));
|
||||
} else {
|
||||
// console.log("key", b, "value", a);
|
||||
return a[b];
|
||||
}
|
||||
} else {
|
||||
// console.log("null or undef");
|
||||
return a;
|
||||
}
|
||||
}, obj);
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
async function get (projectId, sequence, opts = {}) {
|
||||
const client = await setSurvey(projectId);
|
||||
|
||||
const sortFields = [
|
||||
"sequence", "sailline", "line", "point", "tstamp"
|
||||
];
|
||||
const sortKey = opts.sortBy && sortFields.includes(opts.sortBy) && opts.sortBy || "tstamp";
|
||||
const sortDir = opts.sortDesc == "false" ? "ASC" : "DESC";
|
||||
const offset = Math.abs((opts.page-1)*opts.itemsPerPage) || 0;
|
||||
const limit = Math.abs(Number(opts.itemsPerPage)) || null;
|
||||
|
||||
const restriction = sequence != 0
|
||||
? "sequence = $3"
|
||||
: "TRUE OR $3";
|
||||
|
||||
const text = `
|
||||
SELECT
|
||||
sequence, sailline, line, point, tstamp,
|
||||
objRefRaw, objRefFinal,
|
||||
geometryPreplot::json,
|
||||
geometryRaw::json,
|
||||
geometryFinal::json,
|
||||
errorRaw::json,
|
||||
errorFinal::json,
|
||||
jsonb_path_query(meta::jsonb, $4) as meta
|
||||
FROM sequences_detail
|
||||
WHERE ${restriction}
|
||||
ORDER BY ${sortKey} ${sortDir}
|
||||
OFFSET $1
|
||||
LIMIT $2;
|
||||
`;
|
||||
|
||||
const values = [offset, limit, sequence, opts.path || "$"];
|
||||
const res = await client.query(text, values);
|
||||
client.release();
|
||||
|
||||
if (opts.project) {
|
||||
const tokens = opts.project.split(/\s*[,;:\s]\s*/).filter(e => e.length);
|
||||
const project = tokens.map(i => i.replace(/^([^.]+)\..*$/, "$1"));
|
||||
return res.rows.map( r =>
|
||||
Object.fromEntries(Object.entries(r).filter(entry => project.includes(entry[0])))
|
||||
);
|
||||
// const deep = tokens.filter(i => i.includes("."));
|
||||
// console.log("tokens", tokens, "project", project, "deep", deep);
|
||||
// if (deep.length) {
|
||||
// return res.rows.map( r => {
|
||||
// const o = Object.fromEntries(Object.entries(r).filter(entry => project.includes(entry[0])))
|
||||
// deep.forEach(path => {
|
||||
// console.log("path", path, path.split(".")[0]);
|
||||
// console.log("object", o);
|
||||
// console.log("result", thinout(path, o));
|
||||
// return o[path.split(".")[0]] = thinout(path, o)
|
||||
// })
|
||||
// console.log("Object", o);
|
||||
// return o;
|
||||
// });
|
||||
// } else {
|
||||
// return res.rows.map( r =>
|
||||
// Object.fromEntries(Object.entries(r).filter(entry => project.includes(entry[0])))
|
||||
// );
|
||||
// }
|
||||
} else {
|
||||
return res.rows;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = get;
|
||||
|
||||
@@ -56,7 +56,7 @@ function transform (events, sequences, opts = {}) {
|
||||
"EntryType": "Start of line recording",
|
||||
"EntryTypeId": 3000,
|
||||
"Comment": event.remarks.toString(),
|
||||
"Heading": Number(sequence.azimuth.toFixed(1)),
|
||||
"Heading": Number(sequence.azimuth.toFixed(2)),
|
||||
"IsReshoot": sequence.reshoot, // FIXME Add this property to sequence object
|
||||
"IsUndershoot": false,
|
||||
// NOTE https://gitlab.com/wgp/dougal/software/-/issues/12#note_419162674
|
||||
@@ -217,7 +217,7 @@ function transform (events, sequences, opts = {}) {
|
||||
const fsp = Object.assign({}, SequenceObject.Entries[fgspIndex], {
|
||||
"EntryType": "Start of line recording",
|
||||
"EntryTypeId": 3000,
|
||||
"Heading": Number(sequence.azimuth.toFixed(1)),
|
||||
"Heading": Number(sequence.azimuth.toFixed(2)),
|
||||
"IsReshoot": sequence.reshoot,
|
||||
"IsUndershoot": false,
|
||||
"LineNumber": sequence.line.toString(),
|
||||
@@ -235,7 +235,7 @@ function transform (events, sequences, opts = {}) {
|
||||
"Comment": sequence.fsp_final
|
||||
? "(First preplot shot found in P1)"
|
||||
: "(First shot found in P1)",
|
||||
"Heading": Number(sequence.azimuth.toFixed(1)),
|
||||
"Heading": Number(sequence.azimuth.toFixed(2)),
|
||||
"IsReshoot": sequence.reshoot, // FIXME Add this property to sequence object
|
||||
"IsUndershoot": false,
|
||||
// NOTE https://gitlab.com/wgp/dougal/software/-/issues/12#note_419162674
|
||||
|
||||
162
lib/www/server/package-lock.json
generated
162
lib/www/server/package-lock.json
generated
@@ -261,9 +261,6 @@
|
||||
},
|
||||
"bin": {
|
||||
"rimraf": "bin.js"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/@mapbox/node-pre-gyp/node_modules/semver": {
|
||||
@@ -386,11 +383,6 @@
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"supports-color": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/agent-base/node_modules/ms": {
|
||||
@@ -435,7 +427,7 @@
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz",
|
||||
"integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"normalize-path": "^3.0.0",
|
||||
"picomatch": "^2.0.4"
|
||||
@@ -615,7 +607,7 @@
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.1.0.tgz",
|
||||
"integrity": "sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
@@ -659,7 +651,7 @@
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
|
||||
"integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"fill-range": "^7.0.1"
|
||||
},
|
||||
@@ -891,7 +883,7 @@
|
||||
"version": "3.4.3",
|
||||
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.3.tgz",
|
||||
"integrity": "sha512-DtM3g7juCXQxFVSNPNByEC2+NImtBuxQQvWlHunpJIS5Ocr0lG306cC7FCi7cEA0fzmybPUIl4txBIobk1gGOQ==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"anymatch": "~3.1.1",
|
||||
"braces": "~3.0.2",
|
||||
@@ -1095,8 +1087,7 @@
|
||||
"version": "3.6.5",
|
||||
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.6.5.tgz",
|
||||
"integrity": "sha512-vZVEEwZoIsI+vPEuoF9Iqf5H7/M3eeQqWlQnYa8FSKKePuYTf5MWnxb5SDAzCa60b3JBRS5g9b+Dq7b1y/RCrA==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/core-util-is": {
|
||||
"version": "1.0.2",
|
||||
@@ -1473,7 +1464,8 @@
|
||||
"esprima": "^4.0.1",
|
||||
"estraverse": "^5.2.0",
|
||||
"esutils": "^2.0.2",
|
||||
"optionator": "^0.8.1"
|
||||
"optionator": "^0.8.1",
|
||||
"source-map": "~0.6.1"
|
||||
},
|
||||
"bin": {
|
||||
"escodegen": "bin/escodegen.js",
|
||||
@@ -1481,9 +1473,6 @@
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"source-map": "~0.6.1"
|
||||
}
|
||||
},
|
||||
"node_modules/esprima": {
|
||||
@@ -1645,7 +1634,7 @@
|
||||
"version": "7.0.1",
|
||||
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
|
||||
"integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"to-regex-range": "^5.0.1"
|
||||
},
|
||||
@@ -1752,6 +1741,7 @@
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz",
|
||||
"integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
@@ -1849,16 +1839,13 @@
|
||||
},
|
||||
"engines": {
|
||||
"node": "*"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/glob-parent": {
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz",
|
||||
"integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"is-glob": "^4.0.1"
|
||||
},
|
||||
@@ -1924,7 +1911,6 @@
|
||||
"version": "5.1.5",
|
||||
"resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz",
|
||||
"integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==",
|
||||
"deprecated": "this library is no longer supported",
|
||||
"dependencies": {
|
||||
"ajv": "^6.12.3",
|
||||
"har-schema": "^2.0.0"
|
||||
@@ -1942,10 +1928,6 @@
|
||||
"fast-json-stable-stringify": "^2.0.0",
|
||||
"json-schema-traverse": "^0.4.1",
|
||||
"uri-js": "^4.2.2"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/epoberezkin"
|
||||
}
|
||||
},
|
||||
"node_modules/har-validator/node_modules/fast-deep-equal": {
|
||||
@@ -2090,11 +2072,6 @@
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"supports-color": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/http-proxy-agent/node_modules/ms": {
|
||||
@@ -2149,11 +2126,6 @@
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"supports-color": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/https-proxy-agent/node_modules/ms": {
|
||||
@@ -2209,7 +2181,7 @@
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
|
||||
"integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"binary-extensions": "^2.0.0"
|
||||
},
|
||||
@@ -2221,7 +2193,7 @@
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
|
||||
"integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
@@ -2239,7 +2211,7 @@
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz",
|
||||
"integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"is-extglob": "^2.1.1"
|
||||
},
|
||||
@@ -2251,7 +2223,7 @@
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
|
||||
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=0.12.0"
|
||||
}
|
||||
@@ -2336,14 +2308,6 @@
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"canvas": "^2.5.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"canvas": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/jsesc": {
|
||||
@@ -2525,9 +2489,7 @@
|
||||
"integrity": "sha512-/xwPEBidtg69Q3HlqPdU3DnrXQOvQU/CCHA1tcDQVzOwm91YMYaILjNp7L4Eaw5Z4sOYdbBz6koWyibppd8Zqw=="
|
||||
},
|
||||
"node_modules/leaflet-headless": {
|
||||
"version": "0.2.6",
|
||||
"resolved": "git+ssh://git@gitlab.com/aaltronav/contrib/leaflet-headless.git#97eaa645f078eb6f17b9a996740e3b8d61df26ed",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"canvas": "^2.8.0",
|
||||
"jsdom": "^16.6.0",
|
||||
@@ -2673,9 +2635,6 @@
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/make-dir/node_modules/semver": {
|
||||
@@ -2796,9 +2755,6 @@
|
||||
"integrity": "sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/minimalistic-assert": {
|
||||
@@ -2995,7 +2951,7 @@
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
|
||||
"integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
@@ -3033,14 +2989,6 @@
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 6.9.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"chokidar": "^3.3.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"chokidar": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/nwsapi": {
|
||||
@@ -3410,7 +3358,7 @@
|
||||
"version": "2.2.2",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz",
|
||||
"integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=8.6"
|
||||
}
|
||||
@@ -3693,7 +3641,7 @@
|
||||
"version": "3.5.0",
|
||||
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz",
|
||||
"integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"picomatch": "^2.2.1"
|
||||
},
|
||||
@@ -3791,7 +3739,6 @@
|
||||
"version": "2.88.2",
|
||||
"resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz",
|
||||
"integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==",
|
||||
"deprecated": "request has been deprecated, see https://github.com/request/request/issues/3142",
|
||||
"dependencies": {
|
||||
"aws-sign2": "~0.7.0",
|
||||
"aws4": "^1.8.0",
|
||||
@@ -4102,21 +4049,7 @@
|
||||
"node_modules/simple-concat": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz",
|
||||
"integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/feross"
|
||||
},
|
||||
{
|
||||
"type": "patreon",
|
||||
"url": "https://www.patreon.com/feross"
|
||||
},
|
||||
{
|
||||
"type": "consulting",
|
||||
"url": "https://feross.org/support"
|
||||
}
|
||||
]
|
||||
"integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q=="
|
||||
},
|
||||
"node_modules/simple-get": {
|
||||
"version": "3.1.0",
|
||||
@@ -4141,7 +4074,6 @@
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
||||
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
|
||||
"devOptional": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
@@ -4178,11 +4110,6 @@
|
||||
"safer-buffer": "^2.0.2",
|
||||
"tweetnacl": "~0.14.0"
|
||||
},
|
||||
"bin": {
|
||||
"sshpk-conv": "bin/sshpk-conv",
|
||||
"sshpk-sign": "bin/sshpk-sign",
|
||||
"sshpk-verify": "bin/sshpk-verify"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
@@ -4396,9 +4323,6 @@
|
||||
},
|
||||
"bin": {
|
||||
"rimraf": "bin.js"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/to-arraybuffer": {
|
||||
@@ -4420,7 +4344,7 @@
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
|
||||
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"is-number": "^7.0.0"
|
||||
},
|
||||
@@ -4620,7 +4544,6 @@
|
||||
"version": "3.4.0",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
|
||||
"integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
|
||||
"deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.",
|
||||
"bin": {
|
||||
"uuid": "bin/uuid"
|
||||
}
|
||||
@@ -4827,18 +4750,6 @@
|
||||
"integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==",
|
||||
"engines": {
|
||||
"node": ">=8.3.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"bufferutil": "^4.0.1",
|
||||
"utf-8-validate": "^5.0.2"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"bufferutil": {
|
||||
"optional": true
|
||||
},
|
||||
"utf-8-validate": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/xml-name-validator": {
|
||||
@@ -5266,7 +5177,7 @@
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz",
|
||||
"integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"normalize-path": "^3.0.0",
|
||||
"picomatch": "^2.0.4"
|
||||
@@ -5441,7 +5352,7 @@
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.1.0.tgz",
|
||||
"integrity": "sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ==",
|
||||
"devOptional": true
|
||||
"dev": true
|
||||
},
|
||||
"bn.js": {
|
||||
"version": "5.1.3",
|
||||
@@ -5479,7 +5390,7 @@
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
|
||||
"integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"fill-range": "^7.0.1"
|
||||
}
|
||||
@@ -5695,7 +5606,7 @@
|
||||
"version": "3.4.3",
|
||||
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.3.tgz",
|
||||
"integrity": "sha512-DtM3g7juCXQxFVSNPNByEC2+NImtBuxQQvWlHunpJIS5Ocr0lG306cC7FCi7cEA0fzmybPUIl4txBIobk1gGOQ==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"anymatch": "~3.1.1",
|
||||
"braces": "~3.0.2",
|
||||
@@ -6333,7 +6244,7 @@
|
||||
"version": "7.0.1",
|
||||
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
|
||||
"integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"to-regex-range": "^5.0.1"
|
||||
}
|
||||
@@ -6416,6 +6327,7 @@
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz",
|
||||
"integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"gauge": {
|
||||
@@ -6497,7 +6409,7 @@
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz",
|
||||
"integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"is-glob": "^4.0.1"
|
||||
}
|
||||
@@ -6784,7 +6696,7 @@
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
|
||||
"integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"binary-extensions": "^2.0.0"
|
||||
}
|
||||
@@ -6793,7 +6705,7 @@
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
|
||||
"integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=",
|
||||
"devOptional": true
|
||||
"dev": true
|
||||
},
|
||||
"is-fullwidth-code-point": {
|
||||
"version": "3.0.0",
|
||||
@@ -6805,7 +6717,7 @@
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz",
|
||||
"integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"is-extglob": "^2.1.1"
|
||||
}
|
||||
@@ -6814,7 +6726,7 @@
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
|
||||
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
|
||||
"devOptional": true
|
||||
"dev": true
|
||||
},
|
||||
"is-potential-custom-element-name": {
|
||||
"version": "1.0.1",
|
||||
@@ -7426,7 +7338,7 @@
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
|
||||
"integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
|
||||
"devOptional": true
|
||||
"dev": true
|
||||
},
|
||||
"npmlog": {
|
||||
"version": "4.1.2",
|
||||
@@ -7772,7 +7684,7 @@
|
||||
"version": "2.2.2",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz",
|
||||
"integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==",
|
||||
"devOptional": true
|
||||
"dev": true
|
||||
},
|
||||
"polished": {
|
||||
"version": "3.6.7",
|
||||
@@ -8014,7 +7926,7 @@
|
||||
"version": "3.5.0",
|
||||
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz",
|
||||
"integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"picomatch": "^2.2.1"
|
||||
}
|
||||
@@ -8388,8 +8300,7 @@
|
||||
"source-map": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
||||
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
|
||||
"devOptional": true
|
||||
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
|
||||
},
|
||||
"split": {
|
||||
"version": "1.0.1",
|
||||
@@ -8619,7 +8530,7 @@
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
|
||||
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"is-number": "^7.0.0"
|
||||
}
|
||||
@@ -8947,8 +8858,7 @@
|
||||
"ws": {
|
||||
"version": "7.4.6",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz",
|
||||
"integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==",
|
||||
"requires": {}
|
||||
"integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A=="
|
||||
},
|
||||
"xml-name-validator": {
|
||||
"version": "3.0.0",
|
||||
|
||||
Reference in New Issue
Block a user