pyarchinit-photogrammetry 1.2.0__tar.gz → 1.2.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pyarchinit_photogrammetry-1.2.0/src/pyarchinit_photogrammetry.egg-info → pyarchinit_photogrammetry-1.2.2}/PKG-INFO +1 -1
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/pyproject.toml +1 -1
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/__init__.py +1 -1
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/engine/multispectral.py +33 -2
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/gui/icons.py +3 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/gui/image_views.py +105 -8
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/gui/main_window.py +199 -4
- pyarchinit_photogrammetry-1.2.2/src/pyarchinit_photogrammetry/gui/multispectral_pane.py +195 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/gui/panels.py +31 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/gui/translations/en.json +57 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/io/qgis_link.py +70 -5
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2/src/pyarchinit_photogrammetry.egg-info}/PKG-INFO +1 -1
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry.egg-info/SOURCES.txt +1 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/tests/test_multispectral.py +94 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/tests/test_ortho.py +4 -2
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/LICENSE +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/README.md +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/setup.cfg +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/ppg_launcher.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/__main__.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/_env.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/_native.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/ai/__init__.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/ai/classify.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/ai/learn.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/ai/sam.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/ai/sam_env.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/ai/sam_worker.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/app.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/archaeo/__init__.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/archaeo/measures.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/archaeo/munsell.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/archaeo/pottery.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/archaeo/proxies.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/archaeo/relief.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/archaeo/sections.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/archaeo/stones.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/archaeo/stratigraphy.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/archaeo/terrain.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/archaeo/timeline.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/archaeo/us_proposal.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/batch.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/bim/__init__.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/bim/ifc.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/cli.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/core/__init__.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/core/camera.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/core/chunk.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/core/crs.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/core/document.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/core/geometry.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/core/marker.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/core/paradata.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/core/pointcloud.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/core/progress.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/core/versions.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/demo.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/doctor.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/engine/__init__.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/engine/bundle.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/engine/chunks.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/engine/cleaning.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/engine/colour.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/engine/confidence.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/engine/crop.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/engine/dem.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/engine/dem_edit.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/engine/dense.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/engine/diagnostics.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/engine/elevation.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/engine/fieldcheck.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/engine/isolate.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/engine/masks.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/engine/meshing.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/engine/nodes.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/engine/normalmap.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/engine/ortho.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/engine/overviews.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/engine/panorama.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/engine/patches.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/engine/precision.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/engine/quality.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/engine/region.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/engine/satellite.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/engine/sfm.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/engine/texturing.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/engine/tiepoints.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/engine/tsdf.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/engine/video.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/engine/visibility.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/gui/__init__.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/gui/__main__.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/gui/batch_dialog.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/gui/calibration_dialog.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/gui/dialogs.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/gui/flow_layout.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/gui/i18n.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/gui/logging_setup.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/gui/matches_dialog.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/gui/model_view.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/gui/nodes_dialog.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/gui/plugins_dialog.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/gui/pottery_dialog.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/gui/resources/logo.svg +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/gui/scene.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/gui/start_page.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/gui/theme.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/gui/timeline_dialog.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/gui/undo.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/gui/update_dialog.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/gui/us_proposal_dialog.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/gui/worker.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/integrations/__init__.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/integrations/metashape.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/integrations/photo3d.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/integrations/webodm.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/io/__init__.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/io/copc.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/io/exif.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/io/exports.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/io/flythrough.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/io/gpkg.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/io/i3s.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/io/mesh_ply.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/io/qfield.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/io/report.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/io/scans.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/io/web.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/markers/__init__.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/markers/gcp.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/markers/refine.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/markers/targets.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/plugins/__init__.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/plugins/api.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/plugins/index.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/plugins/manager.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/plugins/scaffold.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/pyarchinit/__init__.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/pyarchinit/bridge.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/pyarchinit/config.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/pyarchinit/database.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/pyarchinit/demo_db.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/pyarchinit/media.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/pyarchinit/pyarchinit5_schema.sql +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/testing/__init__.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/testing/synthetic.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry/updates.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry.egg-info/dependency_links.txt +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry.egg-info/entry_points.txt +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry.egg-info/requires.txt +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/src/pyarchinit_photogrammetry.egg-info/top_level.txt +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/tests/test_alignment_modes.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/tests/test_archaeo.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/tests/test_batch.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/tests/test_bim_ai.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/tests/test_bundle.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/tests/test_chunks.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/tests/test_classify.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/tests/test_cleaning.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/tests/test_colour.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/tests/test_dem_edit.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/tests/test_dem_fill.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/tests/test_dem_spikes.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/tests/test_depth_mesh.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/tests/test_diagnostics.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/tests/test_elevation.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/tests/test_env.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/tests/test_exports_web.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/tests/test_fieldcheck.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/tests/test_fisheye_panorama.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/tests/test_geometry.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/tests/test_gui.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/tests/test_gui_cleaning.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/tests/test_gui_layers.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/tests/test_gui_patches.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/tests/test_gui_start.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/tests/test_i18n.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/tests/test_i3s.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/tests/test_ifc_strat.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/tests/test_integrations.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/tests/test_isolate.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/tests/test_learn.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/tests/test_marker_refine.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/tests/test_markers.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/tests/test_masks.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/tests/test_measures.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/tests/test_mesh_ply.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/tests/test_meshing_fallback.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/tests/test_munsell.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/tests/test_native.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/tests/test_nodes.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/tests/test_normalmap.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/tests/test_overviews.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/tests/test_packaging.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/tests/test_paradata.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/tests/test_patches.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/tests/test_photos.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/tests/test_pipeline.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/tests/test_plugins.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/tests/test_pottery_demo.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/tests/test_pottery_search.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/tests/test_precision.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/tests/test_proxies.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/tests/test_pyarchinit.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/tests/test_pyarchinit_media.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/tests/test_qfield.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/tests/test_quality_video.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/tests/test_raster_detail.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/tests/test_reference_cameras.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/tests/test_region.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/tests/test_regressions.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/tests/test_relief.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/tests/test_satellite.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/tests/test_scalebars.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/tests/test_scans.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/tests/test_sfm_global_check.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/tests/test_stones.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/tests/test_texture.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/tests/test_tiepoints.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/tests/test_timeline.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/tests/test_tools_3d.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/tests/test_undo.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/tests/test_updates.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/tests/test_us_proposal.py +0 -0
- {pyarchinit_photogrammetry-1.2.0 → pyarchinit_photogrammetry-1.2.2}/tests/test_versions.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pyarchinit-photogrammetry
|
|
3
|
-
Version: 1.2.
|
|
3
|
+
Version: 1.2.2
|
|
4
4
|
Summary: Open-source photogrammetry engine for archaeology and BIM, integrated with PyArchInit
|
|
5
5
|
Author-email: Enzo Cocca <enzo.ccc@gmail.com>
|
|
6
6
|
License-Expression: GPL-2.0-or-later
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "pyarchinit-photogrammetry"
|
|
7
|
-
version = "1.2.
|
|
7
|
+
version = "1.2.2"
|
|
8
8
|
description = "Open-source photogrammetry engine for archaeology and BIM, integrated with PyArchInit"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "GPL-2.0-or-later"
|
|
@@ -32,7 +32,7 @@ from .core import (ARCHAEO_CLASSES, ASPRS_CLASSES, Camera, Chunk, Document, Mark
|
|
|
32
32
|
from .core.progress import Cancelled, Progress, print_progress
|
|
33
33
|
from . import demo # noqa: E402 (ppg.demo.create_demo)
|
|
34
34
|
|
|
35
|
-
__version__ = "1.2.
|
|
35
|
+
__version__ = "1.2.2"
|
|
36
36
|
|
|
37
37
|
__all__ = ["app", "Document", "Chunk", "Camera", "Sensor", "Marker", "Projection", "Reference", "ScaleBar",
|
|
38
38
|
"PointCloud", "ASPRS_CLASSES", "ARCHAEO_CLASSES", "Progress", "Cancelled", "print_progress", "__version__"]
|
|
@@ -607,8 +607,35 @@ def build_reflectance(chunk: Chunk, bands: list[str] | None = None, resolution:
|
|
|
607
607
|
|
|
608
608
|
|
|
609
609
|
# ------------------------------------------------------------------------------ indices and zoning
|
|
610
|
+
def formula_bands(formula: str) -> set[str]:
|
|
611
|
+
"""The band names a formula uses (``np`` and the numbers left out)."""
|
|
612
|
+
import ast
|
|
613
|
+
|
|
614
|
+
try:
|
|
615
|
+
tree = ast.parse(formula, mode="eval")
|
|
616
|
+
except SyntaxError as exc:
|
|
617
|
+
raise ValueError(f"formula {formula!r}: {exc.msg}") from None
|
|
618
|
+
return {n.id for n in ast.walk(tree) if isinstance(n, ast.Name) and n.id != "np"}
|
|
619
|
+
|
|
620
|
+
|
|
621
|
+
def missing_bands(formula: str, available) -> list[str]:
|
|
622
|
+
"""The bands a formula needs that ``available`` has not got (sorted)."""
|
|
623
|
+
return sorted(formula_bands(formula) - set(available))
|
|
624
|
+
|
|
625
|
+
|
|
626
|
+
def available_indices(bands) -> list[str]:
|
|
627
|
+
"""The indices of :data:`INDICES` whose bands are all among ``bands``."""
|
|
628
|
+
return [name for name, (formula, _) in INDICES.items() if not missing_bands(formula, bands)]
|
|
629
|
+
|
|
630
|
+
|
|
610
631
|
def index_values(formula: str, bands: dict[str, np.ndarray]) -> np.ndarray:
|
|
611
|
-
"""``formula`` over the band arrays (names as in :data:`INDICES`); NaN where undefined.
|
|
632
|
+
"""``formula`` over the band arrays (names as in :data:`INDICES`); NaN where undefined. A
|
|
633
|
+
band the captures have not got (the thermal LWIR of a camera without it) is an error that
|
|
634
|
+
names it, not a crash."""
|
|
635
|
+
missing = missing_bands(formula, bands)
|
|
636
|
+
if missing:
|
|
637
|
+
raise ValueError(f"the formula needs the band(s) {', '.join(missing)}, which these captures have not got "
|
|
638
|
+
f"(bands: {', '.join(bands)})")
|
|
612
639
|
names = {k: np.asarray(v, np.float32) for k, v in bands.items()}
|
|
613
640
|
with np.errstate(divide="ignore", invalid="ignore"):
|
|
614
641
|
out = eval(formula, {"__builtins__": {}, "np": np}, names) # noqa: S307 - our own formulas
|
|
@@ -632,6 +659,9 @@ def index_raster(chunk: Chunk, name: str, formula: str | None = None) -> Path:
|
|
|
632
659
|
profile = src.profile.copy()
|
|
633
660
|
out = index_values(formula, bands)
|
|
634
661
|
profile.update(count=1, dtype="float32", nodata=float("nan"))
|
|
662
|
+
if not profile.get("tiled"):
|
|
663
|
+
profile.pop("blockxsize", None)
|
|
664
|
+
profile.pop("blockysize", None)
|
|
635
665
|
target = chunk.data_dir / "multispectral" / f"{name}.tif"
|
|
636
666
|
with rasterio.open(target, "w", **profile) as dst:
|
|
637
667
|
dst.write(out, 1)
|
|
@@ -736,7 +766,8 @@ def export_zoning(chunk: Chunk, name: str, path, classes: int = 3, method: str =
|
|
|
736
766
|
dst.write(cls, 1)
|
|
737
767
|
areas = {k: round(sum(a["area_m2"] for _, a in polys if a["classe"] == k), 2) for k in range(1, classes + 1)}
|
|
738
768
|
summary = {"index": name, "classes": classes, "method": method, "breaks": [round(b, 4) for b in breaks],
|
|
739
|
-
"polygons": len(polys), "area_m2": areas, "file": str(path)
|
|
769
|
+
"polygons": len(polys), "area_m2": areas, "file": str(path),
|
|
770
|
+
"raster": str((chunk.data_dir / "multispectral" / f"{name}_zone.tif").relative_to(chunk.data_dir))}
|
|
740
771
|
ms.setdefault("zoning", {})[name] = summary
|
|
741
772
|
json.dumps(summary) # serialisable
|
|
742
773
|
return summary
|
|
@@ -49,6 +49,9 @@ ICONS = {
|
|
|
49
49
|
"ifc": ("mdi6.office-building-outline", "slate"), "script": ("mdi6.language-python", "blue"),
|
|
50
50
|
"plugin": ("mdi6.puzzle-outline", "violet"),
|
|
51
51
|
"network": ("mdi6.lan-connect", "violet"),
|
|
52
|
+
"multispectral": ("mdi6.leaf", "green"), "run": ("mdi6.play-circle-outline", "green"),
|
|
53
|
+
"index": ("mdi6.sprout-outline", "lime"), "zones": ("mdi6.view-dashboard-outline", "amber"),
|
|
54
|
+
"qgis": ("mdi6.map-search-outline", "lime"),
|
|
52
55
|
# PyArchInit
|
|
53
56
|
"database": ("mdi6.database-outline", "olive"), "publish": ("mdi6.database-arrow-up-outline", "olive"),
|
|
54
57
|
"load": ("mdi6.database-arrow-down-outline", "olive"), "refresh": ("mdi6.refresh", "grey"),
|
|
@@ -15,7 +15,9 @@ from PySide6.QtWidgets import (QGraphicsEllipseItem, QGraphicsPathItem, QGraphic
|
|
|
15
15
|
from .i18n import tr
|
|
16
16
|
|
|
17
17
|
RELIEF_KINDS = ("svf", "openness_pos", "openness_neg", "slrm", "msrm", "multi_hillshade") # archaeo.relief
|
|
18
|
-
SCALAR_KINDS = ("dem", "diff", "sigma", "coverage", "slope", "aspect") + RELIEF_KINDS # one band of values, coloured
|
|
18
|
+
SCALAR_KINDS = ("dem", "diff", "sigma", "coverage", "slope", "aspect", "index", "zones") + RELIEF_KINDS # one band of values, coloured
|
|
19
|
+
ZONE_COLORS = [(214, 96, 77), (247, 203, 120), (178, 223, 138), (26, 152, 80), (0, 109, 44), (69, 117, 180),
|
|
20
|
+
(152, 78, 163), (255, 127, 0), (166, 86, 40), (120, 120, 120)] # the classes of a zoning, 1 = lowest
|
|
19
21
|
US_COLORS = ["#e6194b", "#3cb44b", "#ffe119", "#4363d8", "#f58231", "#911eb4", "#46f0f0", "#f032e6", "#bcf60c",
|
|
20
22
|
"#fabebe", "#008080", "#e6beff", "#9a6324", "#800000", "#aaffc3", "#808000"]
|
|
21
23
|
|
|
@@ -70,6 +72,76 @@ def dem_colors(z: np.ndarray, res: float, limits: tuple[float, float] | None = N
|
|
|
70
72
|
return rgba
|
|
71
73
|
|
|
72
74
|
|
|
75
|
+
def index_limits(z: np.ndarray) -> tuple[float, float]:
|
|
76
|
+
"""The ends of the colour ramp of a vegetation index: the 2nd and 98th percentiles, within
|
|
77
|
+
-1 and 1 for the normalised indices."""
|
|
78
|
+
valid = np.isfinite(z)
|
|
79
|
+
if not valid.any():
|
|
80
|
+
return -1.0, 1.0
|
|
81
|
+
lo, hi = np.percentile(z[valid], [2, 98])
|
|
82
|
+
if lo >= -1.0 and hi <= 1.0:
|
|
83
|
+
lo, hi = max(lo, -1.0), min(hi, 1.0)
|
|
84
|
+
return float(lo), float(hi) if hi > lo else float(lo) + 1e-6
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def index_colors(z: np.ndarray, limits: tuple[float, float] | None = None) -> np.ndarray:
|
|
88
|
+
"""A vegetation index: brown-red where low, pale yellow in the middle, dark green where
|
|
89
|
+
high (the ramp of the agronomic maps); transparent where no data."""
|
|
90
|
+
valid = np.isfinite(z)
|
|
91
|
+
rgba = np.zeros(z.shape + (4,), np.uint8)
|
|
92
|
+
if not valid.any():
|
|
93
|
+
return rgba
|
|
94
|
+
lo, hi = limits if limits is not None else index_limits(z)
|
|
95
|
+
t = np.clip((np.where(valid, z, lo) - lo) / max(hi - lo, 1e-9), 0, 1)
|
|
96
|
+
stops = np.array([[0.0, 165, 0, 38], [0.25, 244, 109, 67], [0.5, 254, 224, 139], [0.75, 102, 189, 99],
|
|
97
|
+
[1.0, 0, 104, 55]])
|
|
98
|
+
rgba[..., :3] = np.stack([np.interp(t, stops[:, 0], stops[:, k]) for k in (1, 2, 3)], -1).astype(np.uint8)
|
|
99
|
+
rgba[..., 3] = np.where(valid, 255, 0)
|
|
100
|
+
return rgba
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def zone_colors(c: np.ndarray) -> np.ndarray:
|
|
104
|
+
"""The classes of a zoning (1, 2, ...), one colour each; transparent where no class."""
|
|
105
|
+
valid = np.isfinite(c) & (c > 0)
|
|
106
|
+
rgba = np.zeros(c.shape + (4,), np.uint8)
|
|
107
|
+
if not valid.any():
|
|
108
|
+
return rgba
|
|
109
|
+
k = (np.where(valid, c, 1).astype(int) - 1) % len(ZONE_COLORS)
|
|
110
|
+
rgba[..., :3] = np.asarray(ZONE_COLORS, np.uint8)[k]
|
|
111
|
+
rgba[..., 3] = np.where(valid, 255, 0)
|
|
112
|
+
return rgba
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def band_limits(path, bands: tuple[int, int, int]) -> list[tuple[float, float]]:
|
|
116
|
+
"""The 2nd-98th percentile stretch of three bands of a multi-band raster, read at a reduced
|
|
117
|
+
size: the same for the whole raster and for the details."""
|
|
118
|
+
import rasterio
|
|
119
|
+
from rasterio.enums import Resampling
|
|
120
|
+
|
|
121
|
+
out = []
|
|
122
|
+
with rasterio.open(path) as src:
|
|
123
|
+
f = max(1, int(np.ceil(max(src.width, src.height) / 2048)))
|
|
124
|
+
for b in bands:
|
|
125
|
+
a = src.read(b, out_shape=(max(src.height // f, 1), max(src.width // f, 1)), resampling=Resampling.nearest,
|
|
126
|
+
masked=True).astype(np.float64).filled(np.nan)
|
|
127
|
+
v = a[np.isfinite(a)]
|
|
128
|
+
lo, hi = (np.percentile(v, [2, 98]) if v.size > 16 else (0.0, 1.0))
|
|
129
|
+
out.append((float(lo), float(hi) if hi > lo else float(lo) + 1e-6))
|
|
130
|
+
return out
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def stretched_rgba(arr: np.ndarray, limits: list[tuple[float, float]]) -> np.ndarray:
|
|
134
|
+
"""Three float bands ``(h, w, 3)`` stretched between their limits into RGBA, transparent
|
|
135
|
+
where a band has no value."""
|
|
136
|
+
h, w = arr.shape[:2]
|
|
137
|
+
rgba = np.zeros((h, w, 4), np.uint8)
|
|
138
|
+
valid = np.isfinite(arr).all(axis=-1)
|
|
139
|
+
for k, (lo, hi) in enumerate(limits[:3]):
|
|
140
|
+
rgba[..., k] = np.clip((np.nan_to_num(arr[..., k], nan=lo) - lo) / max(hi - lo, 1e-9) * 255, 0, 255).astype(np.uint8)
|
|
141
|
+
rgba[..., 3] = np.where(valid, 255, 0)
|
|
142
|
+
return rgba
|
|
143
|
+
|
|
144
|
+
|
|
73
145
|
def diff_colors(d: np.ndarray, limit: float | None = None) -> tuple[np.ndarray, float]:
|
|
74
146
|
"""Differences between two DEMs: blue below zero, white at zero, red above (symmetric,
|
|
75
147
|
saturated at ``limit``, default the 98th percentile of the absolute differences). Returns
|
|
@@ -191,10 +263,13 @@ def relief_colors(z: np.ndarray, kind: str, limits: tuple[float, float] | None =
|
|
|
191
263
|
|
|
192
264
|
|
|
193
265
|
def read_window_rgba(path, kind: str, window: tuple[int, int, int, int], out_shape: tuple[int, int],
|
|
194
|
-
dem_range: tuple[float, float] | None = None, diff_limit: float | None = None
|
|
266
|
+
dem_range: tuple[float, float] | None = None, diff_limit: float | None = None,
|
|
267
|
+
bands: tuple[int, int, int] | None = None, limits: list | None = None) -> np.ndarray:
|
|
195
268
|
"""RGBA ``(h, w, 4)`` of a window ``(col, row, width, height)`` of a raster read at
|
|
196
269
|
``out_shape`` (the overviews of the file are used when smaller), coloured like the whole
|
|
197
|
-
raster: orthomosaic bands as they are, DEM tinted and shaded, difference of DEMs diverging
|
|
270
|
+
raster: orthomosaic bands as they are, DEM tinted and shaded, difference of DEMs diverging,
|
|
271
|
+
a vegetation index on its ramp, a zoning by class, a reflectance orthomosaic by three of its
|
|
272
|
+
bands (1-based ``bands``) stretched between ``limits``."""
|
|
198
273
|
import rasterio
|
|
199
274
|
from rasterio.enums import Resampling
|
|
200
275
|
from rasterio.windows import Window
|
|
@@ -205,7 +280,7 @@ def read_window_rgba(path, kind: str, window: tuple[int, int, int, int], out_sha
|
|
|
205
280
|
win = Window(col, row, width, height)
|
|
206
281
|
if kind in SCALAR_KINDS:
|
|
207
282
|
z = src.read(1, window=win, out_shape=(h, w), masked=True,
|
|
208
|
-
resampling=Resampling.nearest if kind in ("coverage", "aspect") else Resampling.average
|
|
283
|
+
resampling=Resampling.nearest if kind in ("coverage", "aspect", "zones") else Resampling.average
|
|
209
284
|
).astype(np.float64).filled(np.nan)
|
|
210
285
|
if kind == "slope":
|
|
211
286
|
return slope_colors(z, diff_limit)[0]
|
|
@@ -217,10 +292,19 @@ def read_window_rgba(path, kind: str, window: tuple[int, int, int, int], out_sha
|
|
|
217
292
|
return sigma_colors(z, diff_limit)[0]
|
|
218
293
|
if kind == "coverage":
|
|
219
294
|
return coverage_colors(z)
|
|
295
|
+
if kind == "index":
|
|
296
|
+
return index_colors(z, dem_range)
|
|
297
|
+
if kind == "zones":
|
|
298
|
+
return zone_colors(z)
|
|
220
299
|
if kind in RELIEF_KINDS:
|
|
221
300
|
return relief_colors(z, kind, dem_range)
|
|
222
301
|
res = abs(src.transform.a) * width / max(w, 1)
|
|
223
302
|
return dem_colors(z, res, dem_range)
|
|
303
|
+
if kind == "reflectance":
|
|
304
|
+
bands = bands or (1, 2, 3)
|
|
305
|
+
arr = src.read(list(bands), window=win, out_shape=(3, h, w), resampling=Resampling.average,
|
|
306
|
+
masked=True).astype(np.float64).filled(np.nan)
|
|
307
|
+
return stretched_rgba(np.moveaxis(arr, 0, -1), limits or band_limits(path, bands))
|
|
224
308
|
n = min(src.count, 4)
|
|
225
309
|
arr = np.moveaxis(src.read(list(range(1, n + 1)), window=win, out_shape=(n, h, w),
|
|
226
310
|
resampling=Resampling.average), 0, -1)
|
|
@@ -388,7 +472,7 @@ class RasterView(_ZoomView):
|
|
|
388
472
|
self.layers_changed.emit()
|
|
389
473
|
|
|
390
474
|
# ----------------------------------------------------------- data
|
|
391
|
-
def load(self, path: str | Path, kind: str, max_side: int = 6000) -> None:
|
|
475
|
+
def load(self, path: str | Path, kind: str, max_side: int = 6000, bands=None) -> None:
|
|
392
476
|
import rasterio
|
|
393
477
|
from rasterio.enums import Resampling
|
|
394
478
|
|
|
@@ -430,9 +514,21 @@ class RasterView(_ZoomView):
|
|
|
430
514
|
elif kind in RELIEF_KINDS: # the stretch of the whole raster, also for the detail
|
|
431
515
|
self._dem_range = relief_limits(self.z, kind)
|
|
432
516
|
rgba = relief_colors(self.z, kind, self._dem_range)
|
|
517
|
+
elif kind == "index": # a vegetation index: its ramp between the percentiles of the whole raster
|
|
518
|
+
self._dem_range = index_limits(self.z)
|
|
519
|
+
rgba = index_colors(self.z, self._dem_range)
|
|
520
|
+
elif kind == "zones": # the classes of a zoning
|
|
521
|
+
rgba = zone_colors(self.z)
|
|
433
522
|
else:
|
|
434
523
|
self._dem_range = dem_limits(self.z)
|
|
435
524
|
rgba = dem_colors(self.z, abs(self.tr.a), self._dem_range)
|
|
525
|
+
elif kind == "reflectance": # three bands of a float multi-band raster, stretched
|
|
526
|
+
self.z = None
|
|
527
|
+
self._bands = tuple(int(b) for b in (bands or (1, 2, 3)))[:3]
|
|
528
|
+
self._band_limits = band_limits(path, self._bands)
|
|
529
|
+
arr = src.read(list(self._bands), out_shape=(3, h, w), resampling=Resampling.average,
|
|
530
|
+
masked=True).astype(np.float64).filled(np.nan)
|
|
531
|
+
rgba = stretched_rgba(np.moveaxis(arr, 0, -1), self._band_limits)
|
|
436
532
|
else:
|
|
437
533
|
self.z = None
|
|
438
534
|
n = min(src.count, 4)
|
|
@@ -502,7 +598,7 @@ class RasterView(_ZoomView):
|
|
|
502
598
|
self._detail_key = (window, shape)
|
|
503
599
|
self._detail_gen += 1
|
|
504
600
|
job = (self._detail_gen, str(self.path), self.kind, window, shape, scene, self._dem_range,
|
|
505
|
-
self.diff_limit)
|
|
601
|
+
self.diff_limit, getattr(self, "_bands", None), getattr(self, "_band_limits", None))
|
|
506
602
|
if wait:
|
|
507
603
|
self._show_detail(*self._read_detail(job))
|
|
508
604
|
else:
|
|
@@ -510,8 +606,9 @@ class RasterView(_ZoomView):
|
|
|
510
606
|
|
|
511
607
|
@staticmethod
|
|
512
608
|
def _read_detail(job):
|
|
513
|
-
gen, path, kind, window, shape, scene, dem_range, diff_limit = job
|
|
514
|
-
|
|
609
|
+
gen, path, kind, window, shape, scene, dem_range, diff_limit, bands, limits = job
|
|
610
|
+
kind = kind if kind in SCALAR_KINDS or kind == "reflectance" else "ortho"
|
|
611
|
+
rgba = read_window_rgba(path, kind, window, shape, dem_range, diff_limit, bands=bands, limits=limits)
|
|
515
612
|
return gen, to_qimage(rgba), scene
|
|
516
613
|
|
|
517
614
|
def _read_detail_async(self, job) -> None:
|
|
@@ -279,6 +279,19 @@ class MainWindow(QMainWindow):
|
|
|
279
279
|
self.proxies_pane.quote_requested.connect(self.quote_proxies)
|
|
280
280
|
self.proxies_pane.us_requested.connect(self._us_form_from_proxy)
|
|
281
281
|
self.proxies_pane.delete_requested.connect(self.delete_proxies)
|
|
282
|
+
from .multispectral_pane import MultispectralPane
|
|
283
|
+
|
|
284
|
+
self.ms_pane = MultispectralPane()
|
|
285
|
+
self.left_tabs.addTab(self.ms_pane, icon("multispectral"), tr("Multispettrale"))
|
|
286
|
+
self.ms_pane.add_requested.connect(self.ms_add)
|
|
287
|
+
self.ms_pane.align_requested.connect(self.align_photos)
|
|
288
|
+
self.ms_pane.surface_requested.connect(self.ms_build_surface)
|
|
289
|
+
self.ms_pane.panel_requested.connect(self.ms_panel)
|
|
290
|
+
self.ms_pane.reflectance_requested.connect(self.ms_reflectance)
|
|
291
|
+
self.ms_pane.show_requested.connect(self.ms_show)
|
|
292
|
+
self.ms_pane.index_requested.connect(self.ms_compute_index)
|
|
293
|
+
self.ms_pane.zoning_requested.connect(self.ms_zoning)
|
|
294
|
+
self.ms_pane.qgis_requested.connect(self.open_in_qgis)
|
|
282
295
|
self.sections_pane = SectionsPane()
|
|
283
296
|
self.sections_pane.zoom_requested.connect(self._zoom_section)
|
|
284
297
|
self.sections_pane.publish_requested.connect(self._publish_section)
|
|
@@ -933,6 +946,7 @@ class MainWindow(QMainWindow):
|
|
|
933
946
|
step(0.95, tr("Sezioni e disegni"))
|
|
934
947
|
self._load_sections()
|
|
935
948
|
self.proxies_pane.set_proxies(self.chunk.meta.get("proxies", []) if self.chunk else [])
|
|
949
|
+
self.ms_pane.set_state(self.chunk)
|
|
936
950
|
if self.history is None:
|
|
937
951
|
self.history = UndoHistory(self._undo_state, self._undo_apply, lambda: self.chunk,
|
|
938
952
|
on_change=self._undo_actions)
|
|
@@ -963,10 +977,12 @@ class MainWindow(QMainWindow):
|
|
|
963
977
|
step(0.6, tr("Ortofoto e DEM"))
|
|
964
978
|
self._load_raster()
|
|
965
979
|
self._update_stack()
|
|
980
|
+
self.ms_pane.set_state(self.chunk)
|
|
966
981
|
|
|
967
|
-
def _load_raster(self, kind: str | None = None, path=None) -> None:
|
|
982
|
+
def _load_raster(self, kind: str | None = None, path=None, bands=None) -> None:
|
|
968
983
|
"""The orthomosaic or the DEM of the chunk in the raster view (``path``: another file of
|
|
969
|
-
that kind, e.g. a kept version or a difference of DEMs
|
|
984
|
+
that kind, e.g. a kept version or a difference of DEMs; ``bands``: the three to show of a
|
|
985
|
+
reflectance orthomosaic) with the drawings on top."""
|
|
970
986
|
ch = self.chunk
|
|
971
987
|
kind = kind or ("ortho" if ch is not None and ch.orthomosaic_path else "dem")
|
|
972
988
|
if path is None:
|
|
@@ -976,7 +992,7 @@ class MainWindow(QMainWindow):
|
|
|
976
992
|
self.ortho_view.tr = None
|
|
977
993
|
self.ortho_view.scene().addText(tr("Costruisci il DEM o l'ortofoto per vederli qui"))
|
|
978
994
|
return
|
|
979
|
-
self.ortho_view.load(path, kind)
|
|
995
|
+
self.ortho_view.load(path, kind, bands=bands)
|
|
980
996
|
if kind == "ortho" and ch.dem_path:
|
|
981
997
|
self.ortho_view.set_dem(ch.dem_path)
|
|
982
998
|
self._raster_version = None
|
|
@@ -1202,6 +1218,153 @@ class MainWindow(QMainWindow):
|
|
|
1202
1218
|
self.run_task("Ortofoto di riflettanza", lambda p: ms.build_reflectance(self.chunk, resolution=v["resolution"], progress=p),
|
|
1203
1219
|
lambda path: self._info(tr("Ortofoto di riflettanza"), str(path)))
|
|
1204
1220
|
|
|
1221
|
+
def ms_build_surface(self) -> None:
|
|
1222
|
+
"""Depth maps, dense cloud and DEM in one step with the default choices: what the
|
|
1223
|
+
reflectance orthomosaic needs, for the multispectral pane."""
|
|
1224
|
+
if not self._need_chunk(aligned=True):
|
|
1225
|
+
return
|
|
1226
|
+
ch = self.chunk
|
|
1227
|
+
|
|
1228
|
+
def job(p):
|
|
1229
|
+
if not (ch.data_dir / "dense" / "stereo" / "depth_maps").exists():
|
|
1230
|
+
ch.build_depth_maps(quality="medium", progress=p.sub(0.0, 0.6))
|
|
1231
|
+
if not ch.product_path("point_cloud"):
|
|
1232
|
+
ch.build_point_cloud(progress=p.sub(0.6, 0.25))
|
|
1233
|
+
ch.build_dem(progress=p.sub(0.85, 0.15))
|
|
1234
|
+
return ch.dem_path
|
|
1235
|
+
|
|
1236
|
+
self.run_task("Superficie per il multispettrale", job,
|
|
1237
|
+
lambda path: self._info(tr("Superficie"), tr("DEM pronto: {0}", path)))
|
|
1238
|
+
|
|
1239
|
+
def _ms_meta(self) -> dict:
|
|
1240
|
+
return (self.chunk.meta.get("multispectral") if self.chunk is not None else None) or {}
|
|
1241
|
+
|
|
1242
|
+
def ms_show(self, kind: str, payload=None) -> None:
|
|
1243
|
+
"""A multispectral product in the raster view: the reflectance orthomosaic by three of
|
|
1244
|
+
its bands (names, false colour by default), an index on its ramp, the zones by class
|
|
1245
|
+
(and their polygons as a layer)."""
|
|
1246
|
+
ms = self._ms_meta()
|
|
1247
|
+
ch = self.chunk
|
|
1248
|
+
if kind == "reflectance":
|
|
1249
|
+
rel = ms.get("reflectance")
|
|
1250
|
+
if not rel:
|
|
1251
|
+
self._warn("Costruisci prima l'ortofoto di riflettanza.")
|
|
1252
|
+
return
|
|
1253
|
+
names = list(ms.get("reflectance_bands") or [])
|
|
1254
|
+
wanted = list(payload) if payload else ["NIR", "Red", "Green"]
|
|
1255
|
+
idx = [names.index(b) + 1 for b in wanted if b in names]
|
|
1256
|
+
if len(idx) < 3:
|
|
1257
|
+
idx = (idx + [1, 2, 3])[:3]
|
|
1258
|
+
self._load_raster("reflectance", ch.data_dir / rel, bands=tuple(idx))
|
|
1259
|
+
self.statusBar().showMessage(tr("Riflettanza: bande {0}", ", ".join(names[i - 1] for i in idx)))
|
|
1260
|
+
elif kind == "index":
|
|
1261
|
+
info = (ms.get("indices") or {}).get(payload or "")
|
|
1262
|
+
if not info:
|
|
1263
|
+
self._warn("Calcola prima l'indice.")
|
|
1264
|
+
return
|
|
1265
|
+
self._load_raster("index", ch.data_dir / info["file"])
|
|
1266
|
+
lo, hi = self.ortho_view._dem_range or (-1.0, 1.0)
|
|
1267
|
+
self.statusBar().showMessage(tr("{0}: colori da {1} (rosso) a {2} (verde)", payload, f"{lo:.2f}", f"{hi:.2f}"))
|
|
1268
|
+
elif kind == "zones":
|
|
1269
|
+
z = (ms.get("zoning") or {}).get(payload or "")
|
|
1270
|
+
if not z or not z.get("raster"):
|
|
1271
|
+
self._warn("Fai prima la zonizzazione.")
|
|
1272
|
+
return
|
|
1273
|
+
self._load_raster("zones", ch.data_dir / z["raster"])
|
|
1274
|
+
self._ms_zone_layer(payload, z)
|
|
1275
|
+
else:
|
|
1276
|
+
return
|
|
1277
|
+
self.show_view(self.ortho_view)
|
|
1278
|
+
|
|
1279
|
+
def _ms_zone_layer(self, name: str, z: dict, limit: int = 3000) -> None:
|
|
1280
|
+
"""The polygons of a zoning as a layer of the raster view (up to ``limit``, the largest)."""
|
|
1281
|
+
from ..io import gpkg
|
|
1282
|
+
|
|
1283
|
+
layer = tr("Zone {0}", name)
|
|
1284
|
+
if layer in self.ortho_view.layers:
|
|
1285
|
+
return
|
|
1286
|
+
path = Path(z.get("file", ""))
|
|
1287
|
+
if not path.exists():
|
|
1288
|
+
return
|
|
1289
|
+
try:
|
|
1290
|
+
rows = gpkg.read_table(path, "zone")
|
|
1291
|
+
except Exception as exc: # noqa: BLE001
|
|
1292
|
+
log.warning("zone %s: %s", path, exc)
|
|
1293
|
+
return
|
|
1294
|
+
rows = sorted(rows, key=lambda r: -(r[1].get("area_m2") or 0))[:limit]
|
|
1295
|
+
from .image_views import ZONE_COLORS
|
|
1296
|
+
|
|
1297
|
+
with self.ortho_view.batch():
|
|
1298
|
+
for geom, attrs in rows:
|
|
1299
|
+
polys = list(geom.geoms) if geom.geom_type == "MultiPolygon" else [geom]
|
|
1300
|
+
for poly in polys:
|
|
1301
|
+
rgb = ZONE_COLORS[(int(attrs.get("classe") or 1) - 1) % len(ZONE_COLORS)]
|
|
1302
|
+
self.ortho_view.add_polygon(list(poly.exterior.coords), label="", color="#%02x%02x%02x" % rgb,
|
|
1303
|
+
fill_alpha=0, layer=layer, attrs=dict(attrs))
|
|
1304
|
+
if len(rows) == limit:
|
|
1305
|
+
self.log_pane.info(tr("Zone {0}: nel layer i {1} poligoni più grandi; tutti sono nel GeoPackage {2}",
|
|
1306
|
+
name, limit, path))
|
|
1307
|
+
|
|
1308
|
+
def ms_compute_index(self, name: str, formula: str = "") -> None:
|
|
1309
|
+
"""An index from the reflectance orthomosaic (a known one, or a formula asked for), then shown."""
|
|
1310
|
+
if not self._need_chunk():
|
|
1311
|
+
return
|
|
1312
|
+
from ..engine import multispectral as ms_mod
|
|
1313
|
+
|
|
1314
|
+
if not self._ms_meta().get("reflectance"):
|
|
1315
|
+
self._warn("Costruisci prima l'ortofoto di riflettanza.")
|
|
1316
|
+
return
|
|
1317
|
+
if name == "formula":
|
|
1318
|
+
text, ok = QInputDialog.getText(self, tr("Formula dell'indice"),
|
|
1319
|
+
tr("Formula sui nomi delle bande ({0}):", ", ".join(self._ms_meta().get("reflectance_bands") or [])),
|
|
1320
|
+
text="(NIR - Red) / (NIR + Red)")
|
|
1321
|
+
if not ok or not text.strip():
|
|
1322
|
+
return
|
|
1323
|
+
formula = text.strip()
|
|
1324
|
+
label, ok = QInputDialog.getText(self, tr("Nome dell'indice"), tr("Nome del raster (senza spazi):"), text="Indice")
|
|
1325
|
+
if not ok or not label.strip():
|
|
1326
|
+
return
|
|
1327
|
+
name = "".join(c for c in label.strip() if c.isalnum() or c in "_-") or "Indice"
|
|
1328
|
+
formula = formula or None
|
|
1329
|
+
used = formula or ms_mod.INDICES.get(name, ("", ""))[0]
|
|
1330
|
+
bands = self._ms_meta().get("reflectance_bands") or self._ms_meta().get("bands") or []
|
|
1331
|
+
try:
|
|
1332
|
+
missing = ms_mod.missing_bands(used, bands) if used else []
|
|
1333
|
+
except ValueError as exc:
|
|
1334
|
+
self._warn(tr("Formula non valida: {0}", exc))
|
|
1335
|
+
return
|
|
1336
|
+
if missing:
|
|
1337
|
+
self._warn(tr("L'indice {0} usa le bande {1}, che queste riprese non hanno (bande: {2}).", name,
|
|
1338
|
+
", ".join(missing), ", ".join(bands)))
|
|
1339
|
+
return
|
|
1340
|
+
self.run_task(tr("Indice {0}", name), lambda p: ms_mod.index_raster(self.chunk, name, formula),
|
|
1341
|
+
lambda path: self.ms_show("index", name))
|
|
1342
|
+
|
|
1343
|
+
def ms_zoning(self, name: str, classes: int = 3, method: str = "quantiles") -> None:
|
|
1344
|
+
"""The zoning of an index: polygons in a GeoPackage beside the project, the class raster
|
|
1345
|
+
shown with the polygons as a layer."""
|
|
1346
|
+
if not self._need_chunk():
|
|
1347
|
+
return
|
|
1348
|
+
from ..engine import multispectral as ms_mod
|
|
1349
|
+
|
|
1350
|
+
if name == "formula" or name not in (self._ms_meta().get("indices") or {}):
|
|
1351
|
+
self._warn("Calcola prima l'indice scelto.")
|
|
1352
|
+
return
|
|
1353
|
+
base = Path(self.doc.path).with_name(f"{Path(self.doc.path).stem}_{name}_zone.gpkg") if self.doc.path \
|
|
1354
|
+
else self.chunk.data_dir / "multispectral" / f"{name}_zone.gpkg"
|
|
1355
|
+
path, _f = QFileDialog.getSaveFileName(self, tr("GeoPackage delle zone"), str(base), "GeoPackage (*.gpkg)")
|
|
1356
|
+
if not path:
|
|
1357
|
+
return
|
|
1358
|
+
|
|
1359
|
+
def done(summary):
|
|
1360
|
+
self.ortho_view.remove_layer(tr("Zone {0}", name)) if tr("Zone {0}", name) in self.ortho_view.layers else None
|
|
1361
|
+
self.ms_show("zones", name)
|
|
1362
|
+
self._info(tr("Zonizzazione"), tr("{0}: {1} poligoni, soglie {2}, in {3}", name, summary["polygons"],
|
|
1363
|
+
", ".join(f"{b:.3g}" for b in summary["breaks"]), path))
|
|
1364
|
+
|
|
1365
|
+
self.run_task(tr("Zonizzazione {0}", name),
|
|
1366
|
+
lambda p: ms_mod.export_zoning(self.chunk, name, path, classes=int(classes), method=method), done)
|
|
1367
|
+
|
|
1205
1368
|
def ms_index(self) -> None:
|
|
1206
1369
|
if not self._need_chunk():
|
|
1207
1370
|
return
|
|
@@ -4571,8 +4734,13 @@ class MainWindow(QMainWindow):
|
|
|
4571
4734
|
self.show_coverage_map()
|
|
4572
4735
|
elif payload in ("dem", "orthomosaic"):
|
|
4573
4736
|
self._show_raster("dem" if payload == "dem" else "ortho")
|
|
4737
|
+
elif isinstance(payload, str) and payload.startswith("ms:"):
|
|
4738
|
+
parts = payload.split(":", 2)
|
|
4739
|
+
self.ms_show(parts[1], parts[2] if len(parts) > 2 else None)
|
|
4574
4740
|
else:
|
|
4575
4741
|
self._show_model(payload)
|
|
4742
|
+
elif kind == "multispectral":
|
|
4743
|
+
self._show_pane(self.left_tabs, self.ms_pane)
|
|
4576
4744
|
elif kind == "version":
|
|
4577
4745
|
self.show_version(*payload)
|
|
4578
4746
|
elif kind == "elevation":
|
|
@@ -6229,10 +6397,37 @@ class MainWindow(QMainWindow):
|
|
|
6229
6397
|
rasters.append({"name": "DEM (ombreggiato)", "path": str(ch.dem_path), "hillshade": True,
|
|
6230
6398
|
"opacity": 0.6, "hidden": True})
|
|
6231
6399
|
rasters.append({"name": "DEM", "path": str(ch.dem_path), "hidden": True})
|
|
6400
|
+
extra_vectors = []
|
|
6401
|
+
ms = self._ms_meta()
|
|
6402
|
+
if ch is not None and ms.get("reflectance") and (ch.data_dir / ms["reflectance"]).exists():
|
|
6403
|
+
names = list(ms.get("reflectance_bands") or [])
|
|
6404
|
+
idx = [names.index(b) + 1 for b in ("NIR", "Red", "Green") if b in names]
|
|
6405
|
+
rasters.append({"name": "Riflettanza (falso colore)", "path": str(ch.data_dir / ms["reflectance"]),
|
|
6406
|
+
"bands": idx if len(idx) == 3 else [1, 2, 3], "hidden": True})
|
|
6407
|
+
for iname, info in (ms.get("indices") or {}).items():
|
|
6408
|
+
p_i = ch.data_dir / info.get("file", "")
|
|
6409
|
+
if p_i.exists():
|
|
6410
|
+
rasters.append({"name": f"Indice {iname}", "path": str(p_i), "ramp": "index", "hidden": True})
|
|
6411
|
+
for zname, z in (ms.get("zoning") or {}).items():
|
|
6412
|
+
if z.get("raster") and (ch.data_dir / z["raster"]).exists():
|
|
6413
|
+
rasters.append({"name": f"Zone {zname} (raster)", "path": str(ch.data_dir / z["raster"]),
|
|
6414
|
+
"ramp": "zones", "classes": z.get("classes", 3), "hidden": True})
|
|
6415
|
+
if z.get("file") and Path(z["file"]).exists():
|
|
6416
|
+
extra_vectors.append({"name": f"Zone {zname}", "path": z["file"], "table": "zone",
|
|
6417
|
+
"geometry": "POLYGON", "color": "#1a9850", "label": "classe"})
|
|
6418
|
+
|
|
6419
|
+
for provider in getattr(self, "qgis_providers", []): # plugins: their rasters and GeoPackage layers
|
|
6420
|
+
try:
|
|
6421
|
+
more_rasters, more_vectors = provider()
|
|
6422
|
+
rasters += list(more_rasters or [])
|
|
6423
|
+
extra_vectors += list(more_vectors or [])
|
|
6424
|
+
except Exception as exc: # noqa: BLE001 - a plugin must not stop the export
|
|
6425
|
+
log.warning("QGIS: layer di un plugin saltati: %s", exc)
|
|
6232
6426
|
|
|
6233
6427
|
def job(p):
|
|
6234
6428
|
p(0.2, "Creazione del progetto QGIS")
|
|
6235
|
-
rep = qgis_link.build_project(project, ch.crs if ch else "", files, rasters, title=name, qgis=q
|
|
6429
|
+
rep = qgis_link.build_project(project, ch.crs if ch else "", files, rasters, title=name, qgis=q,
|
|
6430
|
+
extra_vectors=extra_vectors)
|
|
6236
6431
|
qgis_link.open_in_qgis(project, q)
|
|
6237
6432
|
return rep
|
|
6238
6433
|
|