first map setup
This commit is contained in:
52
.gitignore
vendored
Normal file
52
.gitignore
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
# The directory Mix will write compiled artifacts to.
|
||||
/_build/
|
||||
|
||||
# If you run "mix test --cover", coverage assets end up here.
|
||||
/cover/
|
||||
|
||||
# The directory Mix downloads your dependencies sources to.
|
||||
/deps/
|
||||
|
||||
# Where 3rd-party dependencies like ExDoc output generated docs.
|
||||
/doc/
|
||||
|
||||
# Ignore .fetch files in case you like to edit your project deps locally.
|
||||
/.fetch
|
||||
|
||||
# If the VM crashes, it generates a dump, let's ignore it too.
|
||||
erl_crash.dump
|
||||
|
||||
# Also ignore archive artifacts (built via "mix archive.build").
|
||||
*.ez
|
||||
|
||||
# Temporary files, for example, from tests.
|
||||
/tmp/
|
||||
|
||||
# Ignore package tarball (built via "mix hex.build").
|
||||
docmark-*.tar
|
||||
|
||||
docs/*.pdf
|
||||
data
|
||||
|
||||
# Ignore assets that are produced by build tools.
|
||||
/priv/static/assets/
|
||||
/priv/static/*.js
|
||||
/priv/static/*.html
|
||||
/priv/static/*.css
|
||||
|
||||
# Ignore digested assets cache.
|
||||
/priv/static/cache_manifest.json
|
||||
|
||||
# In case you use Node.js/npm, you want to ignore these.
|
||||
npm-debug.log
|
||||
/assets/node_modules/
|
||||
/ui/node_modules/
|
||||
/ui/.vite/
|
||||
|
||||
*.sublime*
|
||||
|
||||
postgres
|
||||
.direnv
|
||||
|
||||
# Generated code:
|
||||
ui/src/types/api.ts
|
||||
4
requirements.txt
Normal file
4
requirements.txt
Normal file
@@ -0,0 +1,4 @@
|
||||
laspy>=2.0.0
|
||||
scipy>=1.9.0
|
||||
numpy>=1.21.0
|
||||
pyproj>=3.0.0
|
||||
71
shell.nix
Normal file
71
shell.nix
Normal file
@@ -0,0 +1,71 @@
|
||||
# shell.nix
|
||||
/*
|
||||
# Dev env for elixir
|
||||
|
||||
|
||||
## Environment Variables
|
||||
|
||||
- MIX_HOME: Local Mix installation
|
||||
- HEX_HOME: Local Hex package manager
|
||||
- PGDIR: PostgreSQL data directory
|
||||
- PGHOST: Unix domain socket location
|
||||
- DATABASE_URL: PostgreSQL connection string
|
||||
*/
|
||||
|
||||
let
|
||||
# Import nixos-unstable for most packages
|
||||
pkgs = import <nixpkgs> {};
|
||||
# Import nixpkgs-unstable for outliers
|
||||
pkgs-unstable = import <nixpkgs-unstable> {};
|
||||
|
||||
extraPackages = [
|
||||
pkgs.beam.packages.erlang_27.elixir_1_18
|
||||
pkgs.beam.packages.erlang_27.erlang
|
||||
pkgs.elixir_ls
|
||||
pkgs.inotify-tools
|
||||
|
||||
# DB
|
||||
pkgs.postgresql
|
||||
# JS
|
||||
pkgs-unstable.deno
|
||||
# PY
|
||||
pkgs.python3
|
||||
pkgs.python3Packages.laspy
|
||||
pkgs.python3Packages.scipy
|
||||
pkgs.python3Packages.numpy
|
||||
pkgs.python3Packages.pyproj
|
||||
];
|
||||
|
||||
mkShell = pkgs.mkShell;
|
||||
PROJECT_ROOT = builtins.toString ./.;
|
||||
|
||||
hooks = ''
|
||||
# Set up environment variables
|
||||
|
||||
# PostgreSQL configuration
|
||||
export PGDIR=${PROJECT_ROOT}/postgres
|
||||
export PGHOST=$PGDIR
|
||||
export PGDATA=$PGDIR/data
|
||||
export PGLOG=$PGDIR/log
|
||||
export DATABASE_URL="postgresql:///postgres?host=$PGDIR"
|
||||
|
||||
# Create PostgreSQL directories if they don't exist
|
||||
if test ! -d $PGDIR; then
|
||||
mkdir $PGDIR
|
||||
fi
|
||||
|
||||
if [ ! -d $PGDATA ]; then
|
||||
echo 'Initializing postgresql database...'
|
||||
initdb $PGDATA --auth=trust >/dev/null
|
||||
fi
|
||||
|
||||
# Print setup instructions
|
||||
echo "Elixir development environment ready!"
|
||||
echo "To start PostgreSQL, run: postgres -h \'\' -k \'$PGHOST\'"
|
||||
echo "To start the project, run : iex -S mix phx.server"
|
||||
'';
|
||||
|
||||
in mkShell {
|
||||
buildInputs = extraPackages;
|
||||
shellHook = hooks;
|
||||
}
|
||||
159
tooling/las2mound.py
Normal file
159
tooling/las2mound.py
Normal file
@@ -0,0 +1,159 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Convert LAS lidar files to .mound binary format for Three.js rendering.
|
||||
|
||||
Usage:
|
||||
python las_to_mound.py input.las output.mound
|
||||
"""
|
||||
|
||||
import sys
|
||||
import struct
|
||||
import numpy as np
|
||||
from pathlib import Path
|
||||
|
||||
try:
|
||||
import laspy
|
||||
except ImportError:
|
||||
print("Error: laspy not installed. Run: pip install laspy")
|
||||
sys.exit(1)
|
||||
|
||||
try:
|
||||
from scipy.spatial import Delaunay
|
||||
except ImportError:
|
||||
print("Error: scipy not installed. Run: pip install scipy")
|
||||
sys.exit(1)
|
||||
|
||||
try:
|
||||
from pyproj import Transformer
|
||||
except ImportError:
|
||||
print("Error: pyproj not installed. Run: pip install pyproj")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def transform_to_latlon(x, y, z):
|
||||
"""Transform Ohio State Plane coordinates to lat/lon."""
|
||||
print("Transforming coordinates to lat/lon...")
|
||||
|
||||
# Ohio State Plane South (EPSG:3735) in US Survey Feet to WGS84 (EPSG:4326)
|
||||
# Note: Ohio has two zones - North (3734) and South (3735)
|
||||
# Newark is in the North zone
|
||||
transformer = Transformer.from_crs("EPSG:3734", "EPSG:4326", always_xy=True)
|
||||
|
||||
# Transform x,y (easting, northing) to lon, lat
|
||||
lon, lat = transformer.transform(x, y)
|
||||
|
||||
# Convert elevation from US Survey Feet to meters
|
||||
z_meters = z * 0.3048006096012192
|
||||
|
||||
print(f"Transformed to lat/lon bounds: lon[{lon.min():.6f}, {lon.max():.6f}] lat[{lat.min():.6f}, {lat.max():.6f}]")
|
||||
|
||||
return lon, lat, z_meters
|
||||
|
||||
|
||||
def read_las(filepath):
|
||||
"""Read LAS file and extract ground points."""
|
||||
print(f"Reading {filepath}...")
|
||||
las = laspy.read(filepath)
|
||||
|
||||
# Extract coordinates
|
||||
x = las.x
|
||||
y = las.y
|
||||
z = las.z
|
||||
|
||||
# Filter for ground points (classification 2) if available
|
||||
if hasattr(las, 'classification'):
|
||||
ground_mask = las.classification == 2
|
||||
if ground_mask.any():
|
||||
print(f"Filtering {ground_mask.sum()} ground points from {len(x)} total points")
|
||||
x = x[ground_mask]
|
||||
y = y[ground_mask]
|
||||
z = z[ground_mask]
|
||||
|
||||
print(f"Loaded {len(x)} points")
|
||||
return x, y, z
|
||||
|
||||
|
||||
def triangulate_points(x, y, z):
|
||||
"""Perform Delaunay triangulation on XY coordinates."""
|
||||
print("Performing Delaunay triangulation...")
|
||||
|
||||
# Create 2D point array for triangulation
|
||||
points_2d = np.column_stack([x, y])
|
||||
|
||||
# Triangulate
|
||||
tri = Delaunay(points_2d)
|
||||
|
||||
print(f"Generated {len(tri.simplices)} triangles")
|
||||
|
||||
return tri.simplices
|
||||
|
||||
|
||||
def write_mound(filepath, x, y, z, indices):
|
||||
"""Write .mound binary format."""
|
||||
print(f"Writing {filepath}...")
|
||||
|
||||
# Convert to float32
|
||||
positions = np.column_stack([x, y, z]).astype(np.float32)
|
||||
indices = indices.astype(np.uint32)
|
||||
|
||||
# Calculate bounds
|
||||
min_x, min_y, min_z = positions.min(axis=0)
|
||||
max_x, max_y, max_z = positions.max(axis=0)
|
||||
|
||||
point_count = len(positions)
|
||||
triangle_count = len(indices)
|
||||
|
||||
with open(filepath, 'wb') as f:
|
||||
# Header (64 bytes)
|
||||
f.write(b'LIDR') # Magic number (4 bytes)
|
||||
f.write(struct.pack('I', 1)) # Version (4 bytes)
|
||||
f.write(struct.pack('I', point_count)) # Point count (4 bytes)
|
||||
f.write(struct.pack('I', triangle_count)) # Triangle count (4 bytes)
|
||||
f.write(struct.pack('f', min_x)) # Min X (4 bytes)
|
||||
f.write(struct.pack('f', min_y)) # Min Y (4 bytes)
|
||||
f.write(struct.pack('f', min_z)) # Min Z (4 bytes)
|
||||
f.write(struct.pack('f', max_x)) # Max X (4 bytes)
|
||||
f.write(struct.pack('f', max_y)) # Max Y (4 bytes)
|
||||
f.write(struct.pack('f', max_z)) # Max Z (4 bytes)
|
||||
f.write(b'\x00' * 24) # Reserved (24 bytes)
|
||||
|
||||
# Position data
|
||||
f.write(positions.tobytes())
|
||||
|
||||
# Index data
|
||||
f.write(indices.tobytes())
|
||||
|
||||
file_size = Path(filepath).stat().st_size / (1024 * 1024)
|
||||
print(f"Wrote {point_count} points, {triangle_count} triangles ({file_size:.2f} MB)")
|
||||
print(f"Bounds: X[{min_x:.2f}, {max_x:.2f}] Y[{min_y:.2f}, {max_y:.2f}] Z[{min_z:.2f}, {max_z:.2f}]")
|
||||
|
||||
|
||||
def main():
|
||||
if len(sys.argv) != 3:
|
||||
print("Usage: python las_to_mound.py input.las output.mound")
|
||||
sys.exit(1)
|
||||
|
||||
input_file = sys.argv[1]
|
||||
output_file = sys.argv[2]
|
||||
|
||||
if not Path(input_file).exists():
|
||||
print(f"Error: Input file '{input_file}' not found")
|
||||
sys.exit(1)
|
||||
|
||||
# Read LAS
|
||||
x, y, z = read_las(input_file)
|
||||
|
||||
# Transform to lat/lon
|
||||
lon, lat, z_meters = transform_to_latlon(x, y, z)
|
||||
|
||||
# Triangulate (using lon/lat as x/y)
|
||||
indices = triangulate_points(lon, lat, z_meters)
|
||||
|
||||
# Write output (lon as x, lat as y, elevation as z)
|
||||
write_mound(output_file, lon, lat, z_meters, indices)
|
||||
|
||||
print("Done!")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
17
ui/deno.json
Normal file
17
ui/deno.json
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"imports": {
|
||||
"@vitejs/plugin-vue": "npm:@vitejs/plugin-vue@^5",
|
||||
"maplibre-gl": "npm:maplibre-gl@^5.16.0",
|
||||
"three": "npm:three@^0.182.0",
|
||||
"vite": "npm:vite@^7.2.1",
|
||||
"vue": "npm:vue@^3.5.23",
|
||||
"pinia": "npm:pinia@^3.0.4",
|
||||
},
|
||||
"tasks": {
|
||||
"dev": "deno run -A --node-modules-dir npm:vite",
|
||||
"build": "deno run -A --node-modules-dir npm:vite build",
|
||||
"watch": "deno run -A --node-modules-dir npm:vite build --watch",
|
||||
"preview": "deno run -A --node-modules-dir npm:vite preview",
|
||||
},
|
||||
"nodeModulesDir": "auto"
|
||||
}
|
||||
745
ui/deno.lock
generated
Normal file
745
ui/deno.lock
generated
Normal file
@@ -0,0 +1,745 @@
|
||||
{
|
||||
"version": "5",
|
||||
"specifiers": {
|
||||
"npm:@vitejs/plugin-vue@5": "5.2.4_vite@7.2.1__picomatch@4.0.3_vue@3.5.23",
|
||||
"npm:maplibre-gl@^5.16.0": "5.16.0",
|
||||
"npm:pinia@^3.0.4": "3.0.4_vue@3.5.23",
|
||||
"npm:three@0.182": "0.182.0",
|
||||
"npm:vite@*": "7.2.1_picomatch@4.0.3",
|
||||
"npm:vite@^7.2.1": "7.2.1_picomatch@4.0.3",
|
||||
"npm:vue@^3.5.23": "3.5.23"
|
||||
},
|
||||
"npm": {
|
||||
"@babel/helper-string-parser@7.27.1": {
|
||||
"integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA=="
|
||||
},
|
||||
"@babel/helper-validator-identifier@7.28.5": {
|
||||
"integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q=="
|
||||
},
|
||||
"@babel/parser@7.28.5": {
|
||||
"integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==",
|
||||
"dependencies": [
|
||||
"@babel/types"
|
||||
],
|
||||
"bin": true
|
||||
},
|
||||
"@babel/types@7.28.5": {
|
||||
"integrity": "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==",
|
||||
"dependencies": [
|
||||
"@babel/helper-string-parser",
|
||||
"@babel/helper-validator-identifier"
|
||||
]
|
||||
},
|
||||
"@esbuild/aix-ppc64@0.25.12": {
|
||||
"integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==",
|
||||
"os": ["aix"],
|
||||
"cpu": ["ppc64"]
|
||||
},
|
||||
"@esbuild/android-arm64@0.25.12": {
|
||||
"integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==",
|
||||
"os": ["android"],
|
||||
"cpu": ["arm64"]
|
||||
},
|
||||
"@esbuild/android-arm@0.25.12": {
|
||||
"integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==",
|
||||
"os": ["android"],
|
||||
"cpu": ["arm"]
|
||||
},
|
||||
"@esbuild/android-x64@0.25.12": {
|
||||
"integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==",
|
||||
"os": ["android"],
|
||||
"cpu": ["x64"]
|
||||
},
|
||||
"@esbuild/darwin-arm64@0.25.12": {
|
||||
"integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==",
|
||||
"os": ["darwin"],
|
||||
"cpu": ["arm64"]
|
||||
},
|
||||
"@esbuild/darwin-x64@0.25.12": {
|
||||
"integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==",
|
||||
"os": ["darwin"],
|
||||
"cpu": ["x64"]
|
||||
},
|
||||
"@esbuild/freebsd-arm64@0.25.12": {
|
||||
"integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==",
|
||||
"os": ["freebsd"],
|
||||
"cpu": ["arm64"]
|
||||
},
|
||||
"@esbuild/freebsd-x64@0.25.12": {
|
||||
"integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==",
|
||||
"os": ["freebsd"],
|
||||
"cpu": ["x64"]
|
||||
},
|
||||
"@esbuild/linux-arm64@0.25.12": {
|
||||
"integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==",
|
||||
"os": ["linux"],
|
||||
"cpu": ["arm64"]
|
||||
},
|
||||
"@esbuild/linux-arm@0.25.12": {
|
||||
"integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==",
|
||||
"os": ["linux"],
|
||||
"cpu": ["arm"]
|
||||
},
|
||||
"@esbuild/linux-ia32@0.25.12": {
|
||||
"integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==",
|
||||
"os": ["linux"],
|
||||
"cpu": ["ia32"]
|
||||
},
|
||||
"@esbuild/linux-loong64@0.25.12": {
|
||||
"integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==",
|
||||
"os": ["linux"],
|
||||
"cpu": ["loong64"]
|
||||
},
|
||||
"@esbuild/linux-mips64el@0.25.12": {
|
||||
"integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==",
|
||||
"os": ["linux"],
|
||||
"cpu": ["mips64el"]
|
||||
},
|
||||
"@esbuild/linux-ppc64@0.25.12": {
|
||||
"integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==",
|
||||
"os": ["linux"],
|
||||
"cpu": ["ppc64"]
|
||||
},
|
||||
"@esbuild/linux-riscv64@0.25.12": {
|
||||
"integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==",
|
||||
"os": ["linux"],
|
||||
"cpu": ["riscv64"]
|
||||
},
|
||||
"@esbuild/linux-s390x@0.25.12": {
|
||||
"integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==",
|
||||
"os": ["linux"],
|
||||
"cpu": ["s390x"]
|
||||
},
|
||||
"@esbuild/linux-x64@0.25.12": {
|
||||
"integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==",
|
||||
"os": ["linux"],
|
||||
"cpu": ["x64"]
|
||||
},
|
||||
"@esbuild/netbsd-arm64@0.25.12": {
|
||||
"integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==",
|
||||
"os": ["netbsd"],
|
||||
"cpu": ["arm64"]
|
||||
},
|
||||
"@esbuild/netbsd-x64@0.25.12": {
|
||||
"integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==",
|
||||
"os": ["netbsd"],
|
||||
"cpu": ["x64"]
|
||||
},
|
||||
"@esbuild/openbsd-arm64@0.25.12": {
|
||||
"integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==",
|
||||
"os": ["openbsd"],
|
||||
"cpu": ["arm64"]
|
||||
},
|
||||
"@esbuild/openbsd-x64@0.25.12": {
|
||||
"integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==",
|
||||
"os": ["openbsd"],
|
||||
"cpu": ["x64"]
|
||||
},
|
||||
"@esbuild/openharmony-arm64@0.25.12": {
|
||||
"integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==",
|
||||
"os": ["openharmony"],
|
||||
"cpu": ["arm64"]
|
||||
},
|
||||
"@esbuild/sunos-x64@0.25.12": {
|
||||
"integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==",
|
||||
"os": ["sunos"],
|
||||
"cpu": ["x64"]
|
||||
},
|
||||
"@esbuild/win32-arm64@0.25.12": {
|
||||
"integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==",
|
||||
"os": ["win32"],
|
||||
"cpu": ["arm64"]
|
||||
},
|
||||
"@esbuild/win32-ia32@0.25.12": {
|
||||
"integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==",
|
||||
"os": ["win32"],
|
||||
"cpu": ["ia32"]
|
||||
},
|
||||
"@esbuild/win32-x64@0.25.12": {
|
||||
"integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==",
|
||||
"os": ["win32"],
|
||||
"cpu": ["x64"]
|
||||
},
|
||||
"@jridgewell/sourcemap-codec@1.5.5": {
|
||||
"integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="
|
||||
},
|
||||
"@mapbox/geojson-rewind@0.5.2": {
|
||||
"integrity": "sha512-tJaT+RbYGJYStt7wI3cq4Nl4SXxG8W7JDG5DMJu97V25RnbNg3QtQtf+KD+VLjNpWKYsRvXDNmNrBgEETr1ifA==",
|
||||
"dependencies": [
|
||||
"get-stream",
|
||||
"minimist"
|
||||
],
|
||||
"bin": true
|
||||
},
|
||||
"@mapbox/jsonlint-lines-primitives@2.0.2": {
|
||||
"integrity": "sha512-rY0o9A5ECsTQRVhv7tL/OyDpGAoUB4tTvLiW1DSzQGq4bvTPhNw1VpSNjDJc5GFZ2XuyOtSWSVN05qOtcD71qQ=="
|
||||
},
|
||||
"@mapbox/point-geometry@1.1.0": {
|
||||
"integrity": "sha512-YGcBz1cg4ATXDCM/71L9xveh4dynfGmcLDqufR+nQQy3fKwsAZsWd/x4621/6uJaeB9mwOHE6hPeDgXz9uViUQ=="
|
||||
},
|
||||
"@mapbox/tiny-sdf@2.0.7": {
|
||||
"integrity": "sha512-25gQLQMcpivjOSA40g3gO6qgiFPDpWRoMfd+G/GoppPIeP6JDaMMkMrEJnMZhKyyS6iKwVt5YKu02vCUyJM3Ug=="
|
||||
},
|
||||
"@mapbox/unitbezier@0.0.1": {
|
||||
"integrity": "sha512-nMkuDXFv60aBr9soUG5q+GvZYL+2KZHVvsqFCzqnkGEf46U2fvmytHaEVc1/YZbiLn8X+eR3QzX1+dwDO1lxlw=="
|
||||
},
|
||||
"@mapbox/vector-tile@2.0.4": {
|
||||
"integrity": "sha512-AkOLcbgGTdXScosBWwmmD7cDlvOjkg/DetGva26pIRiZPdeJYjYKarIlb4uxVzi6bwHO6EWH82eZ5Nuv4T5DUg==",
|
||||
"dependencies": [
|
||||
"@mapbox/point-geometry",
|
||||
"@types/geojson",
|
||||
"pbf"
|
||||
]
|
||||
},
|
||||
"@mapbox/whoots-js@3.1.0": {
|
||||
"integrity": "sha512-Es6WcD0nO5l+2BOQS4uLfNPYQaNDfbot3X1XUoloz+x0mPDS3eeORZJl06HXjwBG1fOGwCRnzK88LMdxKRrd6Q=="
|
||||
},
|
||||
"@maplibre/maplibre-gl-style-spec@24.4.1": {
|
||||
"integrity": "sha512-UKhA4qv1h30XT768ccSv5NjNCX+dgfoq2qlLVmKejspPcSQTYD4SrVucgqegmYcKcmwf06wcNAa/kRd0NHWbUg==",
|
||||
"dependencies": [
|
||||
"@mapbox/jsonlint-lines-primitives",
|
||||
"@mapbox/unitbezier",
|
||||
"json-stringify-pretty-compact",
|
||||
"minimist",
|
||||
"quickselect",
|
||||
"rw",
|
||||
"tinyqueue"
|
||||
],
|
||||
"bin": true
|
||||
},
|
||||
"@maplibre/mlt@1.1.2": {
|
||||
"integrity": "sha512-SQKdJ909VGROkA6ovJgtHNs9YXV4YXUPS+VaZ50I2Mt951SLlUm2Cv34x5Xwc1HiFlsd3h2Yrs5cn7xzqBmENw==",
|
||||
"dependencies": [
|
||||
"@mapbox/point-geometry"
|
||||
]
|
||||
},
|
||||
"@maplibre/vt-pbf@4.2.0": {
|
||||
"integrity": "sha512-bxrk/kQUwWXZgmqYgwOCnZCMONCRi3MJMqJdza4T3E4AeR5i+VyMnaJ8iDWtWxdfEAJRtrzIOeJtxZSy5mFrFA==",
|
||||
"dependencies": [
|
||||
"@mapbox/point-geometry",
|
||||
"@mapbox/vector-tile",
|
||||
"@types/geojson-vt",
|
||||
"@types/supercluster",
|
||||
"geojson-vt",
|
||||
"pbf",
|
||||
"supercluster"
|
||||
]
|
||||
},
|
||||
"@rollup/rollup-android-arm-eabi@4.52.5": {
|
||||
"integrity": "sha512-8c1vW4ocv3UOMp9K+gToY5zL2XiiVw3k7f1ksf4yO1FlDFQ1C2u72iACFnSOceJFsWskc2WZNqeRhFRPzv+wtQ==",
|
||||
"os": ["android"],
|
||||
"cpu": ["arm"]
|
||||
},
|
||||
"@rollup/rollup-android-arm64@4.52.5": {
|
||||
"integrity": "sha512-mQGfsIEFcu21mvqkEKKu2dYmtuSZOBMmAl5CFlPGLY94Vlcm+zWApK7F/eocsNzp8tKmbeBP8yXyAbx0XHsFNA==",
|
||||
"os": ["android"],
|
||||
"cpu": ["arm64"]
|
||||
},
|
||||
"@rollup/rollup-darwin-arm64@4.52.5": {
|
||||
"integrity": "sha512-takF3CR71mCAGA+v794QUZ0b6ZSrgJkArC+gUiG6LB6TQty9T0Mqh3m2ImRBOxS2IeYBo4lKWIieSvnEk2OQWA==",
|
||||
"os": ["darwin"],
|
||||
"cpu": ["arm64"]
|
||||
},
|
||||
"@rollup/rollup-darwin-x64@4.52.5": {
|
||||
"integrity": "sha512-W901Pla8Ya95WpxDn//VF9K9u2JbocwV/v75TE0YIHNTbhqUTv9w4VuQ9MaWlNOkkEfFwkdNhXgcLqPSmHy0fA==",
|
||||
"os": ["darwin"],
|
||||
"cpu": ["x64"]
|
||||
},
|
||||
"@rollup/rollup-freebsd-arm64@4.52.5": {
|
||||
"integrity": "sha512-QofO7i7JycsYOWxe0GFqhLmF6l1TqBswJMvICnRUjqCx8b47MTo46W8AoeQwiokAx3zVryVnxtBMcGcnX12LvA==",
|
||||
"os": ["freebsd"],
|
||||
"cpu": ["arm64"]
|
||||
},
|
||||
"@rollup/rollup-freebsd-x64@4.52.5": {
|
||||
"integrity": "sha512-jr21b/99ew8ujZubPo9skbrItHEIE50WdV86cdSoRkKtmWa+DDr6fu2c/xyRT0F/WazZpam6kk7IHBerSL7LDQ==",
|
||||
"os": ["freebsd"],
|
||||
"cpu": ["x64"]
|
||||
},
|
||||
"@rollup/rollup-linux-arm-gnueabihf@4.52.5": {
|
||||
"integrity": "sha512-PsNAbcyv9CcecAUagQefwX8fQn9LQ4nZkpDboBOttmyffnInRy8R8dSg6hxxl2Re5QhHBf6FYIDhIj5v982ATQ==",
|
||||
"os": ["linux"],
|
||||
"cpu": ["arm"]
|
||||
},
|
||||
"@rollup/rollup-linux-arm-musleabihf@4.52.5": {
|
||||
"integrity": "sha512-Fw4tysRutyQc/wwkmcyoqFtJhh0u31K+Q6jYjeicsGJJ7bbEq8LwPWV/w0cnzOqR2m694/Af6hpFayLJZkG2VQ==",
|
||||
"os": ["linux"],
|
||||
"cpu": ["arm"]
|
||||
},
|
||||
"@rollup/rollup-linux-arm64-gnu@4.52.5": {
|
||||
"integrity": "sha512-a+3wVnAYdQClOTlyapKmyI6BLPAFYs0JM8HRpgYZQO02rMR09ZcV9LbQB+NL6sljzG38869YqThrRnfPMCDtZg==",
|
||||
"os": ["linux"],
|
||||
"cpu": ["arm64"]
|
||||
},
|
||||
"@rollup/rollup-linux-arm64-musl@4.52.5": {
|
||||
"integrity": "sha512-AvttBOMwO9Pcuuf7m9PkC1PUIKsfaAJ4AYhy944qeTJgQOqJYJ9oVl2nYgY7Rk0mkbsuOpCAYSs6wLYB2Xiw0Q==",
|
||||
"os": ["linux"],
|
||||
"cpu": ["arm64"]
|
||||
},
|
||||
"@rollup/rollup-linux-loong64-gnu@4.52.5": {
|
||||
"integrity": "sha512-DkDk8pmXQV2wVrF6oq5tONK6UHLz/XcEVow4JTTerdeV1uqPeHxwcg7aFsfnSm9L+OO8WJsWotKM2JJPMWrQtA==",
|
||||
"os": ["linux"],
|
||||
"cpu": ["loong64"]
|
||||
},
|
||||
"@rollup/rollup-linux-ppc64-gnu@4.52.5": {
|
||||
"integrity": "sha512-W/b9ZN/U9+hPQVvlGwjzi+Wy4xdoH2I8EjaCkMvzpI7wJUs8sWJ03Rq96jRnHkSrcHTpQe8h5Tg3ZzUPGauvAw==",
|
||||
"os": ["linux"],
|
||||
"cpu": ["ppc64"]
|
||||
},
|
||||
"@rollup/rollup-linux-riscv64-gnu@4.52.5": {
|
||||
"integrity": "sha512-sjQLr9BW7R/ZiXnQiWPkErNfLMkkWIoCz7YMn27HldKsADEKa5WYdobaa1hmN6slu9oWQbB6/jFpJ+P2IkVrmw==",
|
||||
"os": ["linux"],
|
||||
"cpu": ["riscv64"]
|
||||
},
|
||||
"@rollup/rollup-linux-riscv64-musl@4.52.5": {
|
||||
"integrity": "sha512-hq3jU/kGyjXWTvAh2awn8oHroCbrPm8JqM7RUpKjalIRWWXE01CQOf/tUNWNHjmbMHg/hmNCwc/Pz3k1T/j/Lg==",
|
||||
"os": ["linux"],
|
||||
"cpu": ["riscv64"]
|
||||
},
|
||||
"@rollup/rollup-linux-s390x-gnu@4.52.5": {
|
||||
"integrity": "sha512-gn8kHOrku8D4NGHMK1Y7NA7INQTRdVOntt1OCYypZPRt6skGbddska44K8iocdpxHTMMNui5oH4elPH4QOLrFQ==",
|
||||
"os": ["linux"],
|
||||
"cpu": ["s390x"]
|
||||
},
|
||||
"@rollup/rollup-linux-x64-gnu@4.52.5": {
|
||||
"integrity": "sha512-hXGLYpdhiNElzN770+H2nlx+jRog8TyynpTVzdlc6bndktjKWyZyiCsuDAlpd+j+W+WNqfcyAWz9HxxIGfZm1Q==",
|
||||
"os": ["linux"],
|
||||
"cpu": ["x64"]
|
||||
},
|
||||
"@rollup/rollup-linux-x64-musl@4.52.5": {
|
||||
"integrity": "sha512-arCGIcuNKjBoKAXD+y7XomR9gY6Mw7HnFBv5Rw7wQRvwYLR7gBAgV7Mb2QTyjXfTveBNFAtPt46/36vV9STLNg==",
|
||||
"os": ["linux"],
|
||||
"cpu": ["x64"]
|
||||
},
|
||||
"@rollup/rollup-openharmony-arm64@4.52.5": {
|
||||
"integrity": "sha512-QoFqB6+/9Rly/RiPjaomPLmR/13cgkIGfA40LHly9zcH1S0bN2HVFYk3a1eAyHQyjs3ZJYlXvIGtcCs5tko9Cw==",
|
||||
"os": ["openharmony"],
|
||||
"cpu": ["arm64"]
|
||||
},
|
||||
"@rollup/rollup-win32-arm64-msvc@4.52.5": {
|
||||
"integrity": "sha512-w0cDWVR6MlTstla1cIfOGyl8+qb93FlAVutcor14Gf5Md5ap5ySfQ7R9S/NjNaMLSFdUnKGEasmVnu3lCMqB7w==",
|
||||
"os": ["win32"],
|
||||
"cpu": ["arm64"]
|
||||
},
|
||||
"@rollup/rollup-win32-ia32-msvc@4.52.5": {
|
||||
"integrity": "sha512-Aufdpzp7DpOTULJCuvzqcItSGDH73pF3ko/f+ckJhxQyHtp67rHw3HMNxoIdDMUITJESNE6a8uh4Lo4SLouOUg==",
|
||||
"os": ["win32"],
|
||||
"cpu": ["ia32"]
|
||||
},
|
||||
"@rollup/rollup-win32-x64-gnu@4.52.5": {
|
||||
"integrity": "sha512-UGBUGPFp1vkj6p8wCRraqNhqwX/4kNQPS57BCFc8wYh0g94iVIW33wJtQAx3G7vrjjNtRaxiMUylM0ktp/TRSQ==",
|
||||
"os": ["win32"],
|
||||
"cpu": ["x64"]
|
||||
},
|
||||
"@rollup/rollup-win32-x64-msvc@4.52.5": {
|
||||
"integrity": "sha512-TAcgQh2sSkykPRWLrdyy2AiceMckNf5loITqXxFI5VuQjS5tSuw3WlwdN8qv8vzjLAUTvYaH/mVjSFpbkFbpTg==",
|
||||
"os": ["win32"],
|
||||
"cpu": ["x64"]
|
||||
},
|
||||
"@types/estree@1.0.8": {
|
||||
"integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="
|
||||
},
|
||||
"@types/geojson-vt@3.2.5": {
|
||||
"integrity": "sha512-qDO7wqtprzlpe8FfQ//ClPV9xiuoh2nkIgiouIptON9w5jvD/fA4szvP9GBlDVdJ5dldAl0kX/sy3URbWwLx0g==",
|
||||
"dependencies": [
|
||||
"@types/geojson"
|
||||
]
|
||||
},
|
||||
"@types/geojson@7946.0.16": {
|
||||
"integrity": "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg=="
|
||||
},
|
||||
"@types/supercluster@7.1.3": {
|
||||
"integrity": "sha512-Z0pOY34GDFl3Q6hUFYf3HkTwKEE02e7QgtJppBt+beEAxnyOpJua+voGFvxINBHa06GwLFFym7gRPY2SiKIfIA==",
|
||||
"dependencies": [
|
||||
"@types/geojson"
|
||||
]
|
||||
},
|
||||
"@vitejs/plugin-vue@5.2.4_vite@7.2.1__picomatch@4.0.3_vue@3.5.23": {
|
||||
"integrity": "sha512-7Yx/SXSOcQq5HiiV3orevHUFn+pmMB4cgbEkDYgnkUWb0WfeQ/wa2yFv6D5ICiCQOVpjA7vYDXrC7AGO8yjDHA==",
|
||||
"dependencies": [
|
||||
"vite",
|
||||
"vue"
|
||||
]
|
||||
},
|
||||
"@vue/compiler-core@3.5.23": {
|
||||
"integrity": "sha512-nW7THWj5HOp085ROk65LwaoxuzDsjIxr485F4iu63BoxsXoSqKqmsUUoP4A7Gl67DgIgi0zJ8JFgHfvny/74MA==",
|
||||
"dependencies": [
|
||||
"@babel/parser",
|
||||
"@vue/shared",
|
||||
"entities",
|
||||
"estree-walker",
|
||||
"source-map-js"
|
||||
]
|
||||
},
|
||||
"@vue/compiler-dom@3.5.23": {
|
||||
"integrity": "sha512-AT8RMw0vEzzzO0JU5gY0F6iCzaWUIh/aaRVordzMBKXRpoTllTT4kocHDssByPsvodNCfump/Lkdow2mT/O5KQ==",
|
||||
"dependencies": [
|
||||
"@vue/compiler-core",
|
||||
"@vue/shared"
|
||||
]
|
||||
},
|
||||
"@vue/compiler-sfc@3.5.23": {
|
||||
"integrity": "sha512-3QTEUo4qg7FtQwaDJa8ou1CUikx5WTtZlY61rRRDu3lK2ZKrGoAGG8mvDgOpDsQ4A1bez9s+WtBB6DS2KuFCPw==",
|
||||
"dependencies": [
|
||||
"@babel/parser",
|
||||
"@vue/compiler-core",
|
||||
"@vue/compiler-dom",
|
||||
"@vue/compiler-ssr",
|
||||
"@vue/shared",
|
||||
"estree-walker",
|
||||
"magic-string",
|
||||
"postcss",
|
||||
"source-map-js"
|
||||
]
|
||||
},
|
||||
"@vue/compiler-ssr@3.5.23": {
|
||||
"integrity": "sha512-Hld2xphbMjXs9Q9WKxPf2EqmE+Rq/FEDnK/wUBtmYq74HCV4XDdSCheAaB823OQXIIFGq9ig/RbAZkF9s4U0Ow==",
|
||||
"dependencies": [
|
||||
"@vue/compiler-dom",
|
||||
"@vue/shared"
|
||||
]
|
||||
},
|
||||
"@vue/devtools-api@7.7.7": {
|
||||
"integrity": "sha512-lwOnNBH2e7x1fIIbVT7yF5D+YWhqELm55/4ZKf45R9T8r9dE2AIOy8HKjfqzGsoTHFbWbr337O4E0A0QADnjBg==",
|
||||
"dependencies": [
|
||||
"@vue/devtools-kit"
|
||||
]
|
||||
},
|
||||
"@vue/devtools-kit@7.7.7": {
|
||||
"integrity": "sha512-wgoZtxcTta65cnZ1Q6MbAfePVFxfM+gq0saaeytoph7nEa7yMXoi6sCPy4ufO111B9msnw0VOWjPEFCXuAKRHA==",
|
||||
"dependencies": [
|
||||
"@vue/devtools-shared",
|
||||
"birpc",
|
||||
"hookable",
|
||||
"mitt",
|
||||
"perfect-debounce",
|
||||
"speakingurl",
|
||||
"superjson"
|
||||
]
|
||||
},
|
||||
"@vue/devtools-shared@7.7.7": {
|
||||
"integrity": "sha512-+udSj47aRl5aKb0memBvcUG9koarqnxNM5yjuREvqwK6T3ap4mn3Zqqc17QrBFTqSMjr3HK1cvStEZpMDpfdyw==",
|
||||
"dependencies": [
|
||||
"rfdc"
|
||||
]
|
||||
},
|
||||
"@vue/reactivity@3.5.23": {
|
||||
"integrity": "sha512-ji5w0qvrPyBmBx5Ldv4QGNsw0phgRreEvjt0iUf1lei2Sm8//9ZAi78uM2ZjsT5gk0YZilLuoRCIMvtuZlHMJw==",
|
||||
"dependencies": [
|
||||
"@vue/shared"
|
||||
]
|
||||
},
|
||||
"@vue/runtime-core@3.5.23": {
|
||||
"integrity": "sha512-LMB0S6/G7mFJcpQeQaZrbsthFbWrIX8FVTzu5x9U3Ec8YW5MY1CGAnBBHNj+TPOBu3pIbtPpjrXtcaN04X+aBw==",
|
||||
"dependencies": [
|
||||
"@vue/reactivity",
|
||||
"@vue/shared"
|
||||
]
|
||||
},
|
||||
"@vue/runtime-dom@3.5.23": {
|
||||
"integrity": "sha512-r/PYc8W9THzEL0UExpTkV+d31zO+Jid/RMZIDG6aS/NekOEUHuCJkJgftySWZw7JTJO/+q9Kxkg8p+i7Q7Q+ew==",
|
||||
"dependencies": [
|
||||
"@vue/reactivity",
|
||||
"@vue/runtime-core",
|
||||
"@vue/shared",
|
||||
"csstype"
|
||||
]
|
||||
},
|
||||
"@vue/server-renderer@3.5.23_vue@3.5.23": {
|
||||
"integrity": "sha512-NiWZsNCsXA20/VufcrW5u+Trt/PyFlpMmxaB2KERYM8eZgUoKUjXxJQb9ypq+LZ0Sp3XHJGNBR8DkhRnkKAMUw==",
|
||||
"dependencies": [
|
||||
"@vue/compiler-ssr",
|
||||
"@vue/shared",
|
||||
"vue"
|
||||
]
|
||||
},
|
||||
"@vue/shared@3.5.23": {
|
||||
"integrity": "sha512-0YZ1DYuC5o/YJPf6pFdt2KYxVGDxkDbH/1NYJnVJWUkzr8ituBEmFVQRNX2gCaAsFEjEDnLkWpgqlZA7htgS/g=="
|
||||
},
|
||||
"birpc@2.7.0": {
|
||||
"integrity": "sha512-tub/wFGH49vNCm0xraykcY3TcRgX/3JsALYq/Lwrtti+bTyFHkCUAWF5wgYoie8P41wYwig2mIKiqoocr1EkEQ=="
|
||||
},
|
||||
"copy-anything@4.0.5": {
|
||||
"integrity": "sha512-7Vv6asjS4gMOuILabD3l739tsaxFQmC+a7pLZm02zyvs8p977bL3zEgq3yDk5rn9B0PbYgIv++jmHcuUab4RhA==",
|
||||
"dependencies": [
|
||||
"is-what"
|
||||
]
|
||||
},
|
||||
"csstype@3.1.3": {
|
||||
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="
|
||||
},
|
||||
"earcut@3.0.2": {
|
||||
"integrity": "sha512-X7hshQbLyMJ/3RPhyObLARM2sNxxmRALLKx1+NVFFnQ9gKzmCrxm9+uLIAdBcvc8FNLpctqlQ2V6AE92Ol9UDQ=="
|
||||
},
|
||||
"entities@4.5.0": {
|
||||
"integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="
|
||||
},
|
||||
"esbuild@0.25.12": {
|
||||
"integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==",
|
||||
"optionalDependencies": [
|
||||
"@esbuild/aix-ppc64",
|
||||
"@esbuild/android-arm",
|
||||
"@esbuild/android-arm64",
|
||||
"@esbuild/android-x64",
|
||||
"@esbuild/darwin-arm64",
|
||||
"@esbuild/darwin-x64",
|
||||
"@esbuild/freebsd-arm64",
|
||||
"@esbuild/freebsd-x64",
|
||||
"@esbuild/linux-arm",
|
||||
"@esbuild/linux-arm64",
|
||||
"@esbuild/linux-ia32",
|
||||
"@esbuild/linux-loong64",
|
||||
"@esbuild/linux-mips64el",
|
||||
"@esbuild/linux-ppc64",
|
||||
"@esbuild/linux-riscv64",
|
||||
"@esbuild/linux-s390x",
|
||||
"@esbuild/linux-x64",
|
||||
"@esbuild/netbsd-arm64",
|
||||
"@esbuild/netbsd-x64",
|
||||
"@esbuild/openbsd-arm64",
|
||||
"@esbuild/openbsd-x64",
|
||||
"@esbuild/openharmony-arm64",
|
||||
"@esbuild/sunos-x64",
|
||||
"@esbuild/win32-arm64",
|
||||
"@esbuild/win32-ia32",
|
||||
"@esbuild/win32-x64"
|
||||
],
|
||||
"scripts": true,
|
||||
"bin": true
|
||||
},
|
||||
"estree-walker@2.0.2": {
|
||||
"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="
|
||||
},
|
||||
"fdir@6.5.0_picomatch@4.0.3": {
|
||||
"integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
|
||||
"dependencies": [
|
||||
"picomatch"
|
||||
],
|
||||
"optionalPeers": [
|
||||
"picomatch"
|
||||
]
|
||||
},
|
||||
"fsevents@2.3.3": {
|
||||
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
|
||||
"os": ["darwin"],
|
||||
"scripts": true
|
||||
},
|
||||
"geojson-vt@4.0.2": {
|
||||
"integrity": "sha512-AV9ROqlNqoZEIJGfm1ncNjEXfkz2hdFlZf0qkVfmkwdKa8vj7H16YUOT81rJw1rdFhyEDlN2Tds91p/glzbl5A=="
|
||||
},
|
||||
"get-stream@6.0.1": {
|
||||
"integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg=="
|
||||
},
|
||||
"gl-matrix@3.4.4": {
|
||||
"integrity": "sha512-latSnyDNt/8zYUB6VIJ6PCh2jBjJX6gnDsoCZ7LyW7GkqrD51EWwa9qCoGixj8YqBtETQK/xY7OmpTF8xz1DdQ=="
|
||||
},
|
||||
"hookable@5.5.3": {
|
||||
"integrity": "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ=="
|
||||
},
|
||||
"is-what@5.5.0": {
|
||||
"integrity": "sha512-oG7cgbmg5kLYae2N5IVd3jm2s+vldjxJzK1pcu9LfpGuQ93MQSzo0okvRna+7y5ifrD+20FE8FvjusyGaz14fw=="
|
||||
},
|
||||
"json-stringify-pretty-compact@4.0.0": {
|
||||
"integrity": "sha512-3CNZ2DnrpByG9Nqj6Xo8vqbjT4F6N+tb4Gb28ESAZjYZ5yqvmc56J+/kuIwkaAMOyblTQhUW7PxMkUb8Q36N3Q=="
|
||||
},
|
||||
"kdbush@4.0.2": {
|
||||
"integrity": "sha512-WbCVYJ27Sz8zi9Q7Q0xHC+05iwkm3Znipc2XTlrnJbsHMYktW4hPhXUE8Ys1engBrvffoSCqbil1JQAa7clRpA=="
|
||||
},
|
||||
"magic-string@0.30.21": {
|
||||
"integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==",
|
||||
"dependencies": [
|
||||
"@jridgewell/sourcemap-codec"
|
||||
]
|
||||
},
|
||||
"maplibre-gl@5.16.0": {
|
||||
"integrity": "sha512-/VDY89nr4jgLJyzmhy325cG6VUI02WkZ/UfVuDbG/piXzo6ODnM+omDFIwWY8tsEsBG26DNDmNMn3Y2ikHsBiA==",
|
||||
"dependencies": [
|
||||
"@mapbox/geojson-rewind",
|
||||
"@mapbox/jsonlint-lines-primitives",
|
||||
"@mapbox/point-geometry",
|
||||
"@mapbox/tiny-sdf",
|
||||
"@mapbox/unitbezier",
|
||||
"@mapbox/vector-tile",
|
||||
"@mapbox/whoots-js",
|
||||
"@maplibre/maplibre-gl-style-spec",
|
||||
"@maplibre/mlt",
|
||||
"@maplibre/vt-pbf",
|
||||
"@types/geojson",
|
||||
"@types/geojson-vt",
|
||||
"@types/supercluster",
|
||||
"earcut",
|
||||
"geojson-vt",
|
||||
"gl-matrix",
|
||||
"kdbush",
|
||||
"murmurhash-js",
|
||||
"pbf",
|
||||
"potpack",
|
||||
"quickselect",
|
||||
"supercluster",
|
||||
"tinyqueue"
|
||||
]
|
||||
},
|
||||
"minimist@1.2.8": {
|
||||
"integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="
|
||||
},
|
||||
"mitt@3.0.1": {
|
||||
"integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw=="
|
||||
},
|
||||
"murmurhash-js@1.0.0": {
|
||||
"integrity": "sha512-TvmkNhkv8yct0SVBSy+o8wYzXjE4Zz3PCesbfs8HiCXXdcTuocApFv11UWlNFWKYsP2okqrhb7JNlSm9InBhIw=="
|
||||
},
|
||||
"nanoid@3.3.11": {
|
||||
"integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
|
||||
"bin": true
|
||||
},
|
||||
"pbf@4.0.1": {
|
||||
"integrity": "sha512-SuLdBvS42z33m8ejRbInMapQe8n0D3vN/Xd5fmWM3tufNgRQFBpaW2YVJxQZV4iPNqb0vEFvssMEo5w9c6BTIA==",
|
||||
"dependencies": [
|
||||
"resolve-protobuf-schema"
|
||||
],
|
||||
"bin": true
|
||||
},
|
||||
"perfect-debounce@1.0.0": {
|
||||
"integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA=="
|
||||
},
|
||||
"picocolors@1.1.1": {
|
||||
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="
|
||||
},
|
||||
"picomatch@4.0.3": {
|
||||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="
|
||||
},
|
||||
"pinia@3.0.4_vue@3.5.23": {
|
||||
"integrity": "sha512-l7pqLUFTI/+ESXn6k3nu30ZIzW5E2WZF/LaHJEpoq6ElcLD+wduZoB2kBN19du6K/4FDpPMazY2wJr+IndBtQw==",
|
||||
"dependencies": [
|
||||
"@vue/devtools-api",
|
||||
"vue"
|
||||
]
|
||||
},
|
||||
"postcss@8.5.6": {
|
||||
"integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==",
|
||||
"dependencies": [
|
||||
"nanoid",
|
||||
"picocolors",
|
||||
"source-map-js"
|
||||
]
|
||||
},
|
||||
"potpack@2.1.0": {
|
||||
"integrity": "sha512-pcaShQc1Shq0y+E7GqJqvZj8DTthWV1KeHGdi0Z6IAin2Oi3JnLCOfwnCo84qc+HAp52wT9nK9H7FAJp5a44GQ=="
|
||||
},
|
||||
"protocol-buffers-schema@3.6.0": {
|
||||
"integrity": "sha512-TdDRD+/QNdrCGCE7v8340QyuXd4kIWIgapsE2+n/SaGiSSbomYl4TjHlvIoCWRpE7wFt02EpB35VVA2ImcBVqw=="
|
||||
},
|
||||
"quickselect@3.0.0": {
|
||||
"integrity": "sha512-XdjUArbK4Bm5fLLvlm5KpTFOiOThgfWWI4axAZDWg4E/0mKdZyI9tNEfds27qCi1ze/vwTR16kvmmGhRra3c2g=="
|
||||
},
|
||||
"resolve-protobuf-schema@2.1.0": {
|
||||
"integrity": "sha512-kI5ffTiZWmJaS/huM8wZfEMer1eRd7oJQhDuxeCLe3t7N7mX3z94CN0xPxBQxFYQTSNz9T0i+v6inKqSdK8xrQ==",
|
||||
"dependencies": [
|
||||
"protocol-buffers-schema"
|
||||
]
|
||||
},
|
||||
"rfdc@1.4.1": {
|
||||
"integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA=="
|
||||
},
|
||||
"rollup@4.52.5": {
|
||||
"integrity": "sha512-3GuObel8h7Kqdjt0gxkEzaifHTqLVW56Y/bjN7PSQtkKr0w3V/QYSdt6QWYtd7A1xUtYQigtdUfgj1RvWVtorw==",
|
||||
"dependencies": [
|
||||
"@types/estree"
|
||||
],
|
||||
"optionalDependencies": [
|
||||
"@rollup/rollup-android-arm-eabi",
|
||||
"@rollup/rollup-android-arm64",
|
||||
"@rollup/rollup-darwin-arm64",
|
||||
"@rollup/rollup-darwin-x64",
|
||||
"@rollup/rollup-freebsd-arm64",
|
||||
"@rollup/rollup-freebsd-x64",
|
||||
"@rollup/rollup-linux-arm-gnueabihf",
|
||||
"@rollup/rollup-linux-arm-musleabihf",
|
||||
"@rollup/rollup-linux-arm64-gnu",
|
||||
"@rollup/rollup-linux-arm64-musl",
|
||||
"@rollup/rollup-linux-loong64-gnu",
|
||||
"@rollup/rollup-linux-ppc64-gnu",
|
||||
"@rollup/rollup-linux-riscv64-gnu",
|
||||
"@rollup/rollup-linux-riscv64-musl",
|
||||
"@rollup/rollup-linux-s390x-gnu",
|
||||
"@rollup/rollup-linux-x64-gnu",
|
||||
"@rollup/rollup-linux-x64-musl",
|
||||
"@rollup/rollup-openharmony-arm64",
|
||||
"@rollup/rollup-win32-arm64-msvc",
|
||||
"@rollup/rollup-win32-ia32-msvc",
|
||||
"@rollup/rollup-win32-x64-gnu",
|
||||
"@rollup/rollup-win32-x64-msvc",
|
||||
"fsevents"
|
||||
],
|
||||
"bin": true
|
||||
},
|
||||
"rw@1.3.3": {
|
||||
"integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ=="
|
||||
},
|
||||
"source-map-js@1.2.1": {
|
||||
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="
|
||||
},
|
||||
"speakingurl@14.0.1": {
|
||||
"integrity": "sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ=="
|
||||
},
|
||||
"supercluster@8.0.1": {
|
||||
"integrity": "sha512-IiOea5kJ9iqzD2t7QJq/cREyLHTtSmUT6gQsweojg9WH2sYJqZK9SswTu6jrscO6D1G5v5vYZ9ru/eq85lXeZQ==",
|
||||
"dependencies": [
|
||||
"kdbush"
|
||||
]
|
||||
},
|
||||
"superjson@2.2.5": {
|
||||
"integrity": "sha512-zWPTX96LVsA/eVYnqOM2+ofcdPqdS1dAF1LN4TS2/MWuUpfitd9ctTa87wt4xrYnZnkLtS69xpBdSxVBP5Rm6w==",
|
||||
"dependencies": [
|
||||
"copy-anything"
|
||||
]
|
||||
},
|
||||
"three@0.182.0": {
|
||||
"integrity": "sha512-GbHabT+Irv+ihI1/f5kIIsZ+Ef9Sl5A1Y7imvS5RQjWgtTPfPnZ43JmlYI7NtCRDK9zir20lQpfg8/9Yd02OvQ=="
|
||||
},
|
||||
"tinyglobby@0.2.15_picomatch@4.0.3": {
|
||||
"integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==",
|
||||
"dependencies": [
|
||||
"fdir",
|
||||
"picomatch"
|
||||
]
|
||||
},
|
||||
"tinyqueue@3.0.0": {
|
||||
"integrity": "sha512-gRa9gwYU3ECmQYv3lslts5hxuIa90veaEcxDYuu3QGOIAEM2mOZkVHp48ANJuu1CURtRdHKUBY5Lm1tHV+sD4g=="
|
||||
},
|
||||
"vite@7.2.1_picomatch@4.0.3": {
|
||||
"integrity": "sha512-qTl3VF7BvOupTR85Zc561sPEgxyUSNSvTQ9fit7DEMP7yPgvvIGm5Zfa1dOM+kOwWGNviK9uFM9ra77+OjK7lQ==",
|
||||
"dependencies": [
|
||||
"esbuild",
|
||||
"fdir",
|
||||
"picomatch",
|
||||
"postcss",
|
||||
"rollup",
|
||||
"tinyglobby"
|
||||
],
|
||||
"optionalDependencies": [
|
||||
"fsevents"
|
||||
],
|
||||
"bin": true
|
||||
},
|
||||
"vue@3.5.23": {
|
||||
"integrity": "sha512-CfvZv/vI52xUhumUvHtD6iFIS78nGWfX4IJnHfBGhpqMI0CwDq2YEngXOeaBFMRmiArcqczuVrLxurvesTYT9w==",
|
||||
"dependencies": [
|
||||
"@vue/compiler-dom",
|
||||
"@vue/compiler-sfc",
|
||||
"@vue/runtime-dom",
|
||||
"@vue/server-renderer",
|
||||
"@vue/shared"
|
||||
]
|
||||
}
|
||||
},
|
||||
"workspace": {
|
||||
"dependencies": [
|
||||
"npm:@vitejs/plugin-vue@5",
|
||||
"npm:maplibre-gl@^5.16.0",
|
||||
"npm:pinia@^3.0.4",
|
||||
"npm:three@0.182",
|
||||
"npm:vite@^7.2.1",
|
||||
"npm:vue@^3.5.23"
|
||||
]
|
||||
}
|
||||
}
|
||||
12
ui/index.html
Normal file
12
ui/index.html
Normal file
@@ -0,0 +1,12 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>MoundHunters</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
1
ui/public/tiles
Symbolic link
1
ui/public/tiles
Symbolic link
@@ -0,0 +1 @@
|
||||
/home/mark/projects/moundhunters/data/MOUND
|
||||
395
ui/src/App.vue
Normal file
395
ui/src/App.vue
Normal file
@@ -0,0 +1,395 @@
|
||||
<template>
|
||||
<div id="app">
|
||||
<div ref="mapContainer" class="map-container"></div>
|
||||
<canvas ref="threeCanvas" class="three-canvas"></canvas>
|
||||
|
||||
<div class="layer-controls">
|
||||
<div class="control-section">
|
||||
<label>Base Map:</label>
|
||||
<label><input type="radio" value="osm" v-model="baseLayer" @change="updateBaseLayer"> Street</label>
|
||||
<label><input type="radio" value="satellite" v-model="baseLayer" @change="updateBaseLayer"> Satellite</label>
|
||||
</div>
|
||||
|
||||
<div class="control-section">
|
||||
<label>
|
||||
<input type="checkbox" v-model="showOctagon" @change="toggleOctagon">
|
||||
Show Octagon
|
||||
</label>
|
||||
<label>
|
||||
<input type="checkbox" v-model="showLidar" @change="toggleLidar">
|
||||
Show Lidar
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ref, onMounted, onUnmounted } from 'vue';
|
||||
import maplibregl from 'maplibre-gl';
|
||||
import 'maplibre-gl/dist/maplibre-gl.css';
|
||||
import * as THREE from 'three';
|
||||
|
||||
export default {
|
||||
name: 'App',
|
||||
setup() {
|
||||
const mapContainer = ref(null);
|
||||
const threeCanvas = ref(null);
|
||||
let map = null;
|
||||
let scene = null;
|
||||
let camera = null;
|
||||
let renderer = null;
|
||||
let lidarMeshes = [];
|
||||
const baseLayer = ref('osm');
|
||||
const showOctagon = ref(true);
|
||||
const showLidar = ref(true);
|
||||
|
||||
// Newark Octagon coordinates (converted from DMS to decimal)
|
||||
const octagonCoords = [
|
||||
[-82.44133, 40.05431], // 40°03'15.5"N 82°26'28.8"W
|
||||
[-82.44264, 40.05311], // 40°03'11.2"N 82°26'33.5"W
|
||||
[-82.44464, 40.05242], // 40°03'08.7"N 82°26'40.7"W
|
||||
[-82.44631, 40.05342], // 40°03'12.3"N 82°26'46.7"W
|
||||
[-82.44728, 40.05500], // 40°03'18.0"N 82°26'50.2"W
|
||||
[-82.44589, 40.05633], // 40°03'22.8"N 82°26'45.2"W
|
||||
[-82.44389, 40.05697], // 40°03'25.1"N 82°26'38.0"W
|
||||
[-82.44192, 40.05589], // 40°03'21.2"N 82°26'30.9"W
|
||||
[-82.44133, 40.05431], // Close the polygon
|
||||
];
|
||||
|
||||
const octagonCenter = [-82.44383, 40.05469];
|
||||
|
||||
onMounted(() => {
|
||||
map = new maplibregl.Map({
|
||||
container: mapContainer.value,
|
||||
style: {
|
||||
version: 8,
|
||||
sources: {
|
||||
'osm': {
|
||||
type: 'raster',
|
||||
tiles: ['https://tile.openstreetmap.org/{z}/{x}/{y}.png'],
|
||||
tileSize: 256,
|
||||
attribution: '© OpenStreetMap contributors'
|
||||
},
|
||||
'satellite': {
|
||||
type: 'raster',
|
||||
tiles: [
|
||||
'https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}'
|
||||
],
|
||||
tileSize: 256,
|
||||
attribution: 'Esri, Maxar, Earthstar Geographics, USDA FSA, USGS, Aerogrid, IGN, IGP, and the GIS User Community'
|
||||
}
|
||||
},
|
||||
layers: [
|
||||
{
|
||||
id: 'osm-layer',
|
||||
type: 'raster',
|
||||
source: 'osm',
|
||||
layout: { visibility: 'visible' }
|
||||
},
|
||||
{
|
||||
id: 'satellite-layer',
|
||||
type: 'raster',
|
||||
source: 'satellite',
|
||||
layout: { visibility: 'none' }
|
||||
}
|
||||
]
|
||||
},
|
||||
center: octagonCenter,
|
||||
zoom: 15
|
||||
});
|
||||
|
||||
map.on('load', () => {
|
||||
// Add octagon source and layer
|
||||
map.addSource('octagon', {
|
||||
type: 'geojson',
|
||||
data: {
|
||||
type: 'Feature',
|
||||
geometry: {
|
||||
type: 'Polygon',
|
||||
coordinates: [octagonCoords]
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
map.addLayer({
|
||||
id: 'octagon-fill',
|
||||
type: 'fill',
|
||||
source: 'octagon',
|
||||
paint: {
|
||||
'fill-color': '#ff0000',
|
||||
'fill-opacity': 0.2
|
||||
}
|
||||
});
|
||||
|
||||
map.addLayer({
|
||||
id: 'octagon-outline',
|
||||
type: 'line',
|
||||
source: 'octagon',
|
||||
paint: {
|
||||
'line-color': '#ff0000',
|
||||
'line-width': 2
|
||||
}
|
||||
});
|
||||
|
||||
// Initialize Three.js
|
||||
initThreeJS();
|
||||
loadLidarTiles();
|
||||
|
||||
// Update Three.js on map move
|
||||
map.on('move', updateThreeCamera);
|
||||
map.on('zoom', updateThreeCamera);
|
||||
});
|
||||
});
|
||||
|
||||
const updateBaseLayer = () => {
|
||||
if (!map) return;
|
||||
|
||||
if (baseLayer.value === 'osm') {
|
||||
map.setLayoutProperty('osm-layer', 'visibility', 'visible');
|
||||
map.setLayoutProperty('satellite-layer', 'visibility', 'none');
|
||||
} else {
|
||||
map.setLayoutProperty('osm-layer', 'visibility', 'none');
|
||||
map.setLayoutProperty('satellite-layer', 'visibility', 'visible');
|
||||
}
|
||||
};
|
||||
|
||||
const toggleOctagon = () => {
|
||||
if (!map) return;
|
||||
|
||||
const visibility = showOctagon.value ? 'visible' : 'none';
|
||||
map.setLayoutProperty('octagon-fill', 'visibility', visibility);
|
||||
map.setLayoutProperty('octagon-outline', 'visibility', visibility);
|
||||
};
|
||||
|
||||
const toggleLidar = () => {
|
||||
lidarMeshes.forEach(mesh => {
|
||||
mesh.visible = showLidar.value;
|
||||
});
|
||||
if (renderer) renderer.render(scene, camera);
|
||||
};
|
||||
|
||||
const initThreeJS = () => {
|
||||
scene = new THREE.Scene();
|
||||
|
||||
camera = new THREE.OrthographicCamera(
|
||||
-1, 1, 1, -1, 0.1, 10000
|
||||
);
|
||||
camera.position.z = 1;
|
||||
|
||||
renderer = new THREE.WebGLRenderer({
|
||||
canvas: threeCanvas.value,
|
||||
alpha: true,
|
||||
antialias: true
|
||||
});
|
||||
renderer.setSize(window.innerWidth, window.innerHeight);
|
||||
renderer.setClearColor(0x000000, 0);
|
||||
|
||||
const ambientLight = new THREE.AmbientLight(0xffffff, 0.4);
|
||||
scene.add(ambientLight);
|
||||
|
||||
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8);
|
||||
directionalLight.position.set(1, 1, 1);
|
||||
scene.add(directionalLight);
|
||||
|
||||
window.addEventListener('resize', handleResize);
|
||||
|
||||
updateThreeCamera();
|
||||
};
|
||||
|
||||
const handleResize = () => {
|
||||
if (!renderer || !camera) return;
|
||||
renderer.setSize(window.innerWidth, window.innerHeight);
|
||||
updateThreeCamera();
|
||||
};
|
||||
|
||||
const updateThreeCamera = () => {
|
||||
if (!map || !camera || !renderer) return;
|
||||
|
||||
const bounds = map.getBounds();
|
||||
const ne = bounds.getNorthEast();
|
||||
const sw = bounds.getSouthWest();
|
||||
|
||||
camera.left = sw.lng;
|
||||
camera.right = ne.lng;
|
||||
camera.top = ne.lat;
|
||||
camera.bottom = sw.lat;
|
||||
camera.updateProjectionMatrix();
|
||||
|
||||
renderer.render(scene, camera);
|
||||
};
|
||||
|
||||
const parseMoundFile = async (url) => {
|
||||
const response = await fetch(url);
|
||||
const buffer = await response.arrayBuffer();
|
||||
|
||||
const view = new DataView(buffer);
|
||||
let offset = 0;
|
||||
|
||||
const magic = String.fromCharCode(
|
||||
view.getUint8(offset++),
|
||||
view.getUint8(offset++),
|
||||
view.getUint8(offset++),
|
||||
view.getUint8(offset++)
|
||||
);
|
||||
|
||||
if (magic !== 'LIDR') {
|
||||
throw new Error('Invalid .mound file');
|
||||
}
|
||||
|
||||
const version = view.getUint32(offset, true); offset += 4;
|
||||
const pointCount = view.getUint32(offset, true); offset += 4;
|
||||
const triangleCount = view.getUint32(offset, true); offset += 4;
|
||||
const minX = view.getFloat32(offset, true); offset += 4;
|
||||
const minY = view.getFloat32(offset, true); offset += 4;
|
||||
const minZ = view.getFloat32(offset, true); offset += 4;
|
||||
const maxX = view.getFloat32(offset, true); offset += 4;
|
||||
const maxY = view.getFloat32(offset, true); offset += 4;
|
||||
const maxZ = view.getFloat32(offset, true); offset += 4;
|
||||
offset += 24; // Skip reserved
|
||||
|
||||
const positions = new Float32Array(buffer, offset, pointCount * 3);
|
||||
offset += pointCount * 3 * 4;
|
||||
|
||||
const indices = new Uint32Array(buffer, offset, triangleCount * 3);
|
||||
|
||||
return {
|
||||
pointCount,
|
||||
triangleCount,
|
||||
bounds: { minX, minY, minZ, maxX, maxY, maxZ },
|
||||
positions,
|
||||
indices
|
||||
};
|
||||
};
|
||||
|
||||
const loadLidarTiles = async () => {
|
||||
const tiles = [
|
||||
'BS19820747',
|
||||
'BS19820748',
|
||||
'BS19830747',
|
||||
'BS19830748'
|
||||
];
|
||||
|
||||
for (const tileName of tiles) {
|
||||
try {
|
||||
console.log(`Loading ${tileName}...`);
|
||||
const data = await parseMoundFile(`/tiles/${tileName}.mound`);
|
||||
|
||||
console.log(`${tileName} bounds:`, data.bounds);
|
||||
console.log(`First few positions:`, data.positions.slice(0, 15));
|
||||
|
||||
const geometry = new THREE.BufferGeometry();
|
||||
geometry.setAttribute('position', new THREE.BufferAttribute(data.positions, 3));
|
||||
geometry.setIndex(new THREE.BufferAttribute(data.indices, 1));
|
||||
geometry.computeVertexNormals();
|
||||
|
||||
const material = new THREE.MeshLambertMaterial({
|
||||
color: 0x8B7355,
|
||||
side: THREE.DoubleSide,
|
||||
wireframe: false
|
||||
});
|
||||
|
||||
const mesh = new THREE.Mesh(geometry, material);
|
||||
scene.add(mesh);
|
||||
lidarMeshes.push(mesh);
|
||||
|
||||
console.log(`Loaded ${tileName}: ${data.pointCount} points, ${data.triangleCount} triangles`);
|
||||
console.log(`Mesh position:`, mesh.position);
|
||||
console.log(`Mesh in scene:`, scene.children.length);
|
||||
} catch (err) {
|
||||
console.error(`Failed to load ${tileName}:`, err);
|
||||
}
|
||||
}
|
||||
|
||||
console.log('Map bounds:', map.getBounds().toArray());
|
||||
console.log('Camera:', camera);
|
||||
|
||||
updateThreeCamera();
|
||||
};
|
||||
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener('resize', handleResize);
|
||||
if (renderer) {
|
||||
renderer.dispose();
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
mapContainer,
|
||||
threeCanvas,
|
||||
baseLayer,
|
||||
showOctagon,
|
||||
showLidar,
|
||||
updateBaseLayer,
|
||||
toggleOctagon,
|
||||
toggleLidar
|
||||
};
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
#app {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.map-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.three-canvas {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.layer-controls {
|
||||
position: absolute;
|
||||
bottom: 20px;
|
||||
left: 20px;
|
||||
background: white;
|
||||
padding: 15px;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 2px 8px rgba(0,0,0,0.3);
|
||||
font-family: Arial, sans-serif;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.control-section {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.control-section:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.control-section label {
|
||||
display: block;
|
||||
margin: 5px 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.control-section label:first-child {
|
||||
font-weight: bold;
|
||||
margin-bottom: 8px;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.control-section input[type="radio"],
|
||||
.control-section input[type="checkbox"] {
|
||||
margin-right: 8px;
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
6
ui/src/main.js
Normal file
6
ui/src/main.js
Normal file
@@ -0,0 +1,6 @@
|
||||
import { createApp } from 'vue'
|
||||
import App from './App.vue'
|
||||
|
||||
|
||||
const app = createApp(App)
|
||||
app.mount('#app')
|
||||
55
ui/vite.config.js
Normal file
55
ui/vite.config.js
Normal file
@@ -0,0 +1,55 @@
|
||||
import { defineConfig } from 'vite'
|
||||
import { dirname, resolve } from 'node:path'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import vue from '@vitejs/plugin-vue'
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url))
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
vue(),
|
||||
],
|
||||
optimizeDeps: {
|
||||
include: ['vue']
|
||||
},
|
||||
build: {
|
||||
outDir: '../priv/static',
|
||||
emptyOutDir: false,
|
||||
rollupOptions: {
|
||||
input: {
|
||||
main: resolve(__dirname, 'index.html'),
|
||||
},
|
||||
output: {
|
||||
entryFileNames: 'app.js',
|
||||
chunkFileNames: 'chunks/[name].js',
|
||||
assetFileNames: 'assets/[name].[ext]'
|
||||
}
|
||||
}
|
||||
},
|
||||
resolve: {
|
||||
alias: {
|
||||
"@": fileURLToPath(new URL('./src', import.meta.url)),
|
||||
"@stores": fileURLToPath(new URL('./src/stores', import.meta.url)),
|
||||
},
|
||||
},
|
||||
server: {
|
||||
proxy: {
|
||||
'/socket': {
|
||||
target: 'http://localhost:4000',
|
||||
changeOrigin: true,
|
||||
secure: false
|
||||
},
|
||||
'/api': {
|
||||
target: 'http://localhost:4000',
|
||||
changeOrigin: true,
|
||||
secure: false
|
||||
},
|
||||
'/admin': {
|
||||
target: 'http://localhost:4000',
|
||||
changeOrigin: true,
|
||||
secure: false
|
||||
},
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user