mirror of
https://github.com/qgis/QGIS.git
synced 2025-10-03 00:04:47 -04:00
Compare commits
129 Commits
1e7e899d09
...
b5e695f3ad
Author | SHA1 | Date | |
---|---|---|---|
|
b5e695f3ad | ||
|
813b161335 | ||
|
4251616e69 | ||
|
74fd45bbb3 | ||
|
9a7cf8b98a | ||
|
04f7402dc7 | ||
|
50857718a6 | ||
|
c735ee32f9 | ||
|
03b4caab1a | ||
|
709e9b6e71 | ||
|
214eafbde7 | ||
|
7eae104e79 | ||
|
791d397aab | ||
|
48f2d22a0a | ||
|
8906518d27 | ||
|
3cd64c8a6a | ||
|
2a7e8d8d0d | ||
|
320b3afd1c | ||
|
90da1671e9 | ||
|
8779a4ea71 | ||
|
5912a6a69d | ||
|
bbbe278b22 | ||
|
412045d752 | ||
|
cb744707bd | ||
|
6edfbdd66c | ||
|
f6b55063c4 | ||
|
8c0d221f22 | ||
|
4f33b6703f | ||
|
4b6afa446d | ||
|
cbe1a74224 | ||
|
24862b27af | ||
|
d2f68454cc | ||
|
a2c5d4bdf0 | ||
|
b09b717a33 | ||
|
055401fde1 | ||
|
4b8616b4f2 | ||
|
2e07d9829f | ||
|
7d31cc1dab | ||
|
e28d12d058 | ||
|
0f1c0de0f8 | ||
|
82d7ab9b62 | ||
|
8bc1f639bb | ||
|
928bdd8cc4 | ||
|
1db5d1d246 | ||
|
1e3167082c | ||
|
c6accd8517 | ||
|
41962a0a24 | ||
|
0fc8b37487 | ||
|
1c53ab415b | ||
|
579a818ce7 | ||
|
5ed5f51121 | ||
|
62fbbe6392 | ||
|
ac1260d159 | ||
|
d9a4c80aa9 | ||
|
af0208c974 | ||
|
a01770e5cb | ||
|
d63653925c | ||
|
85e00b755b | ||
|
206cf49e41 | ||
|
5cf505c584 | ||
|
a92a318f99 | ||
|
22d6b83351 | ||
|
1f7f070325 | ||
|
9cddecd8be | ||
|
9b58c9d973 | ||
|
6df907a02b | ||
|
7108812a7b | ||
|
45f0e33759 | ||
|
3e06b3d151 | ||
|
8063554822 | ||
|
fb9429fb20 | ||
|
9d369ed282 | ||
|
621be995f6 | ||
|
4d6f204063 | ||
|
36ec7fbcce | ||
|
88d443ac8f | ||
|
656959f5f2 | ||
|
0dbabdf1e1 | ||
|
7a36d05a37 | ||
|
c0a3055b6e | ||
|
d553c935c3 | ||
|
143949770a | ||
|
43ae0b6706 | ||
|
e6746493b9 | ||
|
5f320b4369 | ||
|
3c3a6069c7 | ||
|
bae5d99f67 | ||
|
e233e48826 | ||
|
f580afd7a6 | ||
|
beb3e987f0 | ||
|
a5f3530fae | ||
|
65cfee669c | ||
|
5743ee9c95 | ||
|
3d316b1bb1 | ||
|
652159f079 | ||
|
81e9b9ad47 | ||
|
42feace4f9 | ||
|
c6578b7986 | ||
|
a71ed7cb77 | ||
|
b2ccee0990 | ||
|
091e072c22 | ||
|
85437bccd9 | ||
|
5f9c6ffaff | ||
|
8525448b2a | ||
|
4b5129f940 | ||
|
833aace899 | ||
|
e2a955eee2 | ||
|
9ede69b396 | ||
|
127ff85935 | ||
|
bc9689d09f | ||
|
bd0ebbfe7d | ||
|
bd320e3f29 | ||
|
82136d0c6e | ||
|
15d89d273a | ||
|
131e12668c | ||
|
e9ed10a2a5 | ||
|
270b91524f | ||
|
97d77a9edf | ||
|
d8195bb589 | ||
|
dd647cb16c | ||
|
29a7b983a8 | ||
|
c70a30fa2b | ||
|
f17100d1bb | ||
|
49f44ea494 | ||
|
505701993e | ||
|
2c7980b97d | ||
|
25444cca9f | ||
|
ff528b63cc | ||
|
f3354b205b |
@ -16,3 +16,8 @@ test_core_openclutils
|
|||||||
|
|
||||||
# Relies on a broken/unreliable 3rd party service
|
# Relies on a broken/unreliable 3rd party service
|
||||||
test_core_layerdefinition
|
test_core_layerdefinition
|
||||||
|
|
||||||
|
# Randomly segfault on Ubuntu 25.04, for unknown reasons
|
||||||
|
PyQgsServerApi
|
||||||
|
PyQgsServerWMS
|
||||||
|
PyQgsServerConfigCache
|
||||||
|
@ -109,6 +109,7 @@ cmake \
|
|||||||
-DWITH_QTSERIALPORT=ON \
|
-DWITH_QTSERIALPORT=ON \
|
||||||
-DWITH_PDF4QT=${WITH_PDF4QT} \
|
-DWITH_PDF4QT=${WITH_PDF4QT} \
|
||||||
-DWITH_SFCGAL=${WITH_SFCGAL} \
|
-DWITH_SFCGAL=${WITH_SFCGAL} \
|
||||||
|
-DWITH_INTERNAL_SPATIALINDEX=${WITH_INTERNAL_SPATIALINDEX} \
|
||||||
-DORACLE_INCLUDEDIR=/instantclient_21_16/sdk/include/ \
|
-DORACLE_INCLUDEDIR=/instantclient_21_16/sdk/include/ \
|
||||||
-DORACLE_LIBDIR=/instantclient_21_16/ \
|
-DORACLE_LIBDIR=/instantclient_21_16/ \
|
||||||
-DDISABLE_DEPRECATED=ON \
|
-DDISABLE_DEPRECATED=ON \
|
||||||
|
@ -144,17 +144,6 @@ RUN apt-get update \
|
|||||||
libgdal-dev \
|
libgdal-dev \
|
||||||
libproj-dev
|
libproj-dev
|
||||||
|
|
||||||
# download spatialindex and compile it
|
|
||||||
RUN curl -L https://github.com/libspatialindex/libspatialindex/releases/download/2.0.0/spatialindex-src-2.0.0.tar.gz --output spatialindex-src-2.0.0.tar.gz \
|
|
||||||
&& mkdir spatialindex \
|
|
||||||
&& tar zxf spatialindex-src-2.0.0.tar.gz -C spatialindex --strip-components=1 \
|
|
||||||
&& rm -f spatialindex-src-2.0.0.tar.gz \
|
|
||||||
&& mkdir -p spatialindex/build \
|
|
||||||
&& cd spatialindex/build \
|
|
||||||
&& cmake -GNinja -DCMAKE_INSTALL_PREFIX=/usr/local .. \
|
|
||||||
&& ninja \
|
|
||||||
&& ninja install
|
|
||||||
|
|
||||||
RUN
|
RUN
|
||||||
FROM binary-for-oracle AS binary-only
|
FROM binary-for-oracle AS binary-only
|
||||||
|
|
||||||
|
2
.github/workflows/build-macos-qt6.yml
vendored
2
.github/workflows/build-macos-qt6.yml
vendored
@ -54,7 +54,7 @@ jobs:
|
|||||||
echo $(brew --prefix flex)/bin >> $GITHUB_PATH
|
echo $(brew --prefix flex)/bin >> $GITHUB_PATH
|
||||||
echo $(brew --prefix libtool)/bin >> $GITHUB_PATH
|
echo $(brew --prefix libtool)/bin >> $GITHUB_PATH
|
||||||
|
|
||||||
- uses: actions/setup-python@v5
|
- uses: actions/setup-python@v6
|
||||||
with:
|
with:
|
||||||
python-version: '3.11'
|
python-version: '3.11'
|
||||||
|
|
||||||
|
4
.github/workflows/build_artifact_comment.yml
vendored
4
.github/workflows/build_artifact_comment.yml
vendored
@ -27,7 +27,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: 'Download artifact'
|
- name: 'Download artifact'
|
||||||
id: download_artifact
|
id: download_artifact
|
||||||
uses: actions/github-script@v7
|
uses: actions/github-script@v8
|
||||||
with:
|
with:
|
||||||
script: |
|
script: |
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
@ -71,7 +71,7 @@ jobs:
|
|||||||
unzip data-*.zip
|
unzip data-*.zip
|
||||||
|
|
||||||
- name: 'Post artifact download link as comment on PR'
|
- name: 'Post artifact download link as comment on PR'
|
||||||
uses: actions/github-script@v7
|
uses: actions/github-script@v8
|
||||||
with:
|
with:
|
||||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
script: |
|
script: |
|
||||||
|
@ -18,7 +18,7 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
python ./scripts/get_latest_qgis_versions.py --release="stable" --github_token=${{ secrets.GITHUB_TOKEN }} >> $GITHUB_ENV
|
python ./scripts/get_latest_qgis_versions.py --release="stable" --github_token=${{ secrets.GITHUB_TOKEN }} >> $GITHUB_ENV
|
||||||
- name: Write comment
|
- name: Write comment
|
||||||
uses: actions/github-script@v7
|
uses: actions/github-script@v8
|
||||||
with:
|
with:
|
||||||
script: |
|
script: |
|
||||||
const {ISSUE_BODY, QGIS_VERSION_LTR_PATCH, QGIS_VERSION_STABLE_PATCH} = process.env // Latest released version identified using get_latest_qgis_versions
|
const {ISSUE_BODY, QGIS_VERSION_LTR_PATCH, QGIS_VERSION_STABLE_PATCH} = process.env // Latest released version identified using get_latest_qgis_versions
|
||||||
|
8
.github/workflows/code_layout.yml
vendored
8
.github/workflows/code_layout.yml
vendored
@ -25,7 +25,7 @@ jobs:
|
|||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v5
|
||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
uses: actions/setup-python@v5
|
uses: actions/setup-python@v6
|
||||||
with:
|
with:
|
||||||
python-version: '3.13'
|
python-version: '3.13'
|
||||||
- name: Install requirements
|
- name: Install requirements
|
||||||
@ -162,7 +162,7 @@ jobs:
|
|||||||
silversearcher-ag
|
silversearcher-ag
|
||||||
|
|
||||||
- name: Retrieve changed files
|
- name: Retrieve changed files
|
||||||
uses: tj-actions/changed-files@ed68ef82c095e0d48ec87eccea555d944a631a4c #v46
|
uses: tj-actions/changed-files@24d32ffd492484c1d75e0c0b894501ddb9d30d62 #v46
|
||||||
id: changed_files
|
id: changed_files
|
||||||
with:
|
with:
|
||||||
separator: " "
|
separator: " "
|
||||||
@ -177,7 +177,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
uses: actions/setup-python@v5
|
uses: actions/setup-python@v6
|
||||||
with:
|
with:
|
||||||
python-version: '3.13'
|
python-version: '3.13'
|
||||||
- name: Install Requirements
|
- name: Install Requirements
|
||||||
@ -212,7 +212,7 @@ jobs:
|
|||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v5
|
||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
uses: actions/setup-python@v5
|
uses: actions/setup-python@v6
|
||||||
with:
|
with:
|
||||||
python-version: '3.13'
|
python-version: '3.13'
|
||||||
- name: Run Check
|
- name: Run Check
|
||||||
|
2
.github/workflows/pr-auto-label.yml
vendored
2
.github/workflows/pr-auto-label.yml
vendored
@ -15,6 +15,6 @@ jobs:
|
|||||||
pull-requests: write
|
pull-requests: write
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/labeler@v5
|
- uses: actions/labeler@v6
|
||||||
with:
|
with:
|
||||||
repo-token: "${{ secrets.GITHUB_TOKEN }}"
|
repo-token: "${{ secrets.GITHUB_TOKEN }}"
|
||||||
|
6
.github/workflows/pre-commit.yaml
vendored
6
.github/workflows/pre-commit.yaml
vendored
@ -24,7 +24,7 @@ jobs:
|
|||||||
fetch-depth: 200
|
fetch-depth: 200
|
||||||
|
|
||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
uses: actions/setup-python@v5
|
uses: actions/setup-python@v6
|
||||||
with:
|
with:
|
||||||
python-version: '3.13'
|
python-version: '3.13'
|
||||||
|
|
||||||
@ -66,7 +66,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Listen for `/fix-precommit` comment
|
- name: Listen for `/fix-precommit` comment
|
||||||
if: failure() && github.event_name == 'issue_comment' && github.event.comment.body == '/fix-precommit' && github.event.issue.pull_request
|
if: failure() && github.event_name == 'issue_comment' && github.event.comment.body == '/fix-precommit' && github.event.issue.pull_request
|
||||||
uses: actions/github-script@v7
|
uses: actions/github-script@v8
|
||||||
with:
|
with:
|
||||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
script: |
|
script: |
|
||||||
@ -88,7 +88,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Comment on PR if pre-commit failed
|
- name: Comment on PR if pre-commit failed
|
||||||
if: failure() && github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository
|
if: failure() && github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository
|
||||||
uses: actions/github-script@v7
|
uses: actions/github-script@v8
|
||||||
with:
|
with:
|
||||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
script: |
|
script: |
|
||||||
|
3
.github/workflows/run-tests.yml
vendored
3
.github/workflows/run-tests.yml
vendored
@ -56,6 +56,7 @@ jobs:
|
|||||||
with-sfcgal: ON
|
with-sfcgal: ON
|
||||||
with-compile-commands: ON
|
with-compile-commands: ON
|
||||||
with-model-test: ON
|
with-model-test: ON
|
||||||
|
with-internal-spatialindex: ON
|
||||||
with-pg-test: ON
|
with-pg-test: ON
|
||||||
with-mssql-test: ON
|
with-mssql-test: ON
|
||||||
with-hana-test: ON
|
with-hana-test: ON
|
||||||
@ -80,6 +81,7 @@ jobs:
|
|||||||
with-sfcgal: ON
|
with-sfcgal: ON
|
||||||
with-compile-commands: OFF
|
with-compile-commands: OFF
|
||||||
with-model-test: OFF
|
with-model-test: OFF
|
||||||
|
with-internal-spatialindex: OFF
|
||||||
with-pg-test: OFF
|
with-pg-test: OFF
|
||||||
with-mssql-test: OFF
|
with-mssql-test: OFF
|
||||||
with-hana-test: OFF
|
with-hana-test: OFF
|
||||||
@ -180,6 +182,7 @@ jobs:
|
|||||||
--env WITH_SFCGAL=${{ matrix.with-sfcgal }} \
|
--env WITH_SFCGAL=${{ matrix.with-sfcgal }} \
|
||||||
--env WITH_COMPILE_COMMANDS=${{ matrix.with-compile-commands }} \
|
--env WITH_COMPILE_COMMANDS=${{ matrix.with-compile-commands }} \
|
||||||
--env ENABLE_UNITY_BUILDS=${{ matrix.unity-builds }} \
|
--env ENABLE_UNITY_BUILDS=${{ matrix.unity-builds }} \
|
||||||
|
--env WITH_INTERNAL_SPATIALINDEX=${{ matrix.with-internal-spatialindex }} \
|
||||||
--env WITH_MODEL_TEST=${{ matrix.with-model-test }} \
|
--env WITH_MODEL_TEST=${{ matrix.with-model-test }} \
|
||||||
--env WITH_PG_TEST=${{ matrix.with-pg-test }} \
|
--env WITH_PG_TEST=${{ matrix.with-pg-test }} \
|
||||||
--env WITH_MSSQL_TEST=${{ matrix.with-mssql-test }} \
|
--env WITH_MSSQL_TEST=${{ matrix.with-mssql-test }} \
|
||||||
|
2
.github/workflows/sipify-bot.yml
vendored
2
.github/workflows/sipify-bot.yml
vendored
@ -12,7 +12,7 @@ jobs:
|
|||||||
runs-on: [ubuntu-latest]
|
runs-on: [ubuntu-latest]
|
||||||
steps:
|
steps:
|
||||||
|
|
||||||
- uses: actions/setup-python@v5
|
- uses: actions/setup-python@v6
|
||||||
with:
|
with:
|
||||||
python-version: '3.13'
|
python-version: '3.13'
|
||||||
|
|
||||||
|
2
.github/workflows/stale.yml
vendored
2
.github/workflows/stale.yml
vendored
@ -14,7 +14,7 @@ jobs:
|
|||||||
if: github.repository_owner == 'qgis'
|
if: github.repository_owner == 'qgis'
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/stale@v9
|
- uses: actions/stale@v10
|
||||||
with:
|
with:
|
||||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
stale-pr-message: >
|
stale-pr-message: >
|
||||||
|
4
.github/workflows/write_failure_comment.yml
vendored
4
.github/workflows/write_failure_comment.yml
vendored
@ -24,7 +24,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: 'Download artifact'
|
- name: 'Download artifact'
|
||||||
id: download_artifact
|
id: download_artifact
|
||||||
uses: actions/github-script@v7
|
uses: actions/github-script@v8
|
||||||
with:
|
with:
|
||||||
script: |
|
script: |
|
||||||
let allArtifacts = await github.rest.actions.listWorkflowRunArtifacts({
|
let allArtifacts = await github.rest.actions.listWorkflowRunArtifacts({
|
||||||
@ -58,7 +58,7 @@ jobs:
|
|||||||
|
|
||||||
- name: 'Post test report markdown summary as comment on PR'
|
- name: 'Post test report markdown summary as comment on PR'
|
||||||
if: fromJSON(steps.download_artifact.outputs.artifact_id) > 0
|
if: fromJSON(steps.download_artifact.outputs.artifact_id) > 0
|
||||||
uses: actions/github-script@v7
|
uses: actions/github-script@v8
|
||||||
with:
|
with:
|
||||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
script: |
|
script: |
|
||||||
|
21
external/rectpack2D/LICENSE
vendored
Normal file
21
external/rectpack2D/LICENSE
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2025 Patryk Czachurski
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
217
external/rectpack2D/README.md
vendored
Normal file
217
external/rectpack2D/README.md
vendored
Normal file
@ -0,0 +1,217 @@
|
|||||||
|
<div align="center">
|
||||||
|
|
||||||
|
|
||||||
|
# rectpack2D
|
||||||
|
|
||||||
|
[](https://github.com/TeamHypersomnia/rectpack2D/actions/workflows/cmake-multi-platform.yml)
|
||||||
|
|
||||||
|
**Used in [Assassin's Creed: Valhalla](https://www.youtube.com/watch?v=2KnjDL4DnwM&t=2382s)!**
|
||||||
|
|
||||||
|
**Used by [Skydio](https://pages.skydio.com/rs/784-TUF-591/images/Open%20Source%20Software%20Notice%20v0.2.html), one of the top drone manufacturers!**
|
||||||
|
|
||||||
|
**[2 scientific references](https://scholar.google.com/scholar?hl=en&as_sdt=0%2C5&q=teamhypersomnia&btnG=)!**
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
A header-only 2D rectangle packing library written in modern C++.
|
||||||
|
This is a refactored and **highly optimized** version of the [original library](https://github.com/TeamHypersomnia/rectpack2D/tree/legacy).
|
||||||
|
|
||||||
|
It was originally developed for the needs of [Hypersomnia](https://github.com/TeamHypersomnia/Hypersomnia), a free and open-source multiplayer shooter.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Table of contents
|
||||||
|
|
||||||
|
- [Benchmarks](#benchmarks)
|
||||||
|
- [Usage](#usage)
|
||||||
|
- [Building the example](#building-the-example)
|
||||||
|
* [Windows](#windows)
|
||||||
|
* [Linux](#linux)
|
||||||
|
- [Algorithm](#algorithm)
|
||||||
|
* [Insertion algorithm](#insertion-algorithm)
|
||||||
|
* [Additional heuristics](#additional-heuristics)
|
||||||
|
|
||||||
|
## Benchmarks
|
||||||
|
|
||||||
|
Tests were conducted on a ``Intel(R) Core(TM) i7-4770K CPU @ 3.50GHz``.
|
||||||
|
The binary was built with ``clang 6.0.0``, using an -03 switch.
|
||||||
|
|
||||||
|
### Arbitrary game sprites: 582 subjects.
|
||||||
|
|
||||||
|
**Runtime: 0.8 ms**
|
||||||
|
**Wasted pixels: 10982 (0.24% - equivalent of a 105 x 105 square)**
|
||||||
|
|
||||||
|
Output (1896 x 2382):
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
In color:
|
||||||
|
(black is wasted space)
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### Arbitrary game sprites + Japanese glyphs: 3264 subjects.
|
||||||
|
|
||||||
|
**Runtime: 4 ms**
|
||||||
|
**Wasted pixels: 15538 (0.31% - equivalent of a 125 x 125 square)**
|
||||||
|
|
||||||
|
Output (2116 x 2382):
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
In color:
|
||||||
|
(black is wasted space)
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
|
||||||
|
### Japanese glyphs + some GUI sprites: 3122 subjects.
|
||||||
|
|
||||||
|
**Runtime: 3.5 - 7 ms**
|
||||||
|
**Wasted pixels: 9288 (1.23% - equivalent of a 96 x 96 square)**
|
||||||
|
|
||||||
|
Output (866 x 871):
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
In color:
|
||||||
|
(black is wasted space)
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
This is a header-only library.
|
||||||
|
Just include the ``src/finders_interface.h`` and you should be good to go.
|
||||||
|
|
||||||
|
For an example use, see ``example/main.cpp``.
|
||||||
|
|
||||||
|
## Building the example
|
||||||
|
|
||||||
|
### Windows
|
||||||
|
|
||||||
|
From the repository's folder, run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake -G "Visual Studio 15 2017 Win64" ..
|
||||||
|
````
|
||||||
|
|
||||||
|
Then just build the generated ``.sln`` file using the newest Visual Studio Preview.
|
||||||
|
|
||||||
|
### Linux
|
||||||
|
|
||||||
|
From the repository's folder, run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cmake/build_example.sh Release gcc g++
|
||||||
|
cd build/current
|
||||||
|
ninja run
|
||||||
|
````
|
||||||
|
|
||||||
|
Or, if you want to use clang, run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cmake/build_example.sh Release clang clang++
|
||||||
|
cd build/current
|
||||||
|
ninja run
|
||||||
|
````
|
||||||
|
|
||||||
|
## Algorithm
|
||||||
|
|
||||||
|
### Insertion algorithm
|
||||||
|
|
||||||
|
The library started as an implementation of this algorithm:
|
||||||
|
|
||||||
|
http://blackpawn.com/texts/lightmaps/default.html
|
||||||
|
|
||||||
|
The current version somewhat derives from the concept described there -
|
||||||
|
however, it uses just a **vector of empty spaces, instead of a tree** - this turned out to be a performance breakthrough.
|
||||||
|
|
||||||
|
Given
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
struct rect_xywh {
|
||||||
|
int x;
|
||||||
|
int y;
|
||||||
|
int w;
|
||||||
|
int h;
|
||||||
|
};
|
||||||
|
````
|
||||||
|
|
||||||
|
Let us create a vector and call it empty_spaces.
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
std::vector<rect_xywh> empty_spaces;
|
||||||
|
````
|
||||||
|
|
||||||
|
Given a user-specified initial bin, which is a square of some size S, we initialize the first empty space.
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
empty_spaces.push_back(rect_xywh(0, 0, S, S));
|
||||||
|
````
|
||||||
|
|
||||||
|
Now, we'd like to insert the first image rectangle.
|
||||||
|
|
||||||
|
To do this, we iterate the vector of empty spaces **backwards** and look for an empty space into which the image can fit.
|
||||||
|
For now, we only have the S x S square: let's save the index of this candidate empty space,
|
||||||
|
which is ``candidate_space_index = 0;``
|
||||||
|
|
||||||
|
If our image is strictly smaller than the candidate space, we have something like this:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
The blue is our image rectangle.
|
||||||
|
We now calculate the gray rectangles labeled as "bigger split" and "smaller split",
|
||||||
|
and save them like this:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// Erase the space that we've just inserted to, by swapping and popping.
|
||||||
|
empty_spaces[candidate_space_index] = empty_spaces.back();
|
||||||
|
empty_spaces.pop_back();
|
||||||
|
|
||||||
|
// Save the resultant splits
|
||||||
|
empty_spaces.push_back(bigger_split);
|
||||||
|
empty_spaces.push_back(smaller_split);
|
||||||
|
````
|
||||||
|
|
||||||
|
Notice that we push the smaller split *after* the bigger one.
|
||||||
|
This is fairly important, because later the candidate images will encounter the smaller splits first,
|
||||||
|
which will make better use of empty spaces overall.
|
||||||
|
|
||||||
|
#### Corner cases:
|
||||||
|
|
||||||
|
- If the image dimensions equal the dimensions of the candidate empty space (image fits exactly),
|
||||||
|
- we just delete the space and create no splits.
|
||||||
|
- If the image fits into the candidate empty space, but exactly one of the image dimensions equals the respective dimension of the candidate empty space (e.g. image = 20x40, candidate space = 30x40)
|
||||||
|
- we delete the space and create a single split. In this case a 10x40 space.
|
||||||
|
|
||||||
|
To see the complete, modular procedure for calculating the splits (along with the corner cases),
|
||||||
|
[see this source](src/rectpack2D/insert_and_split.h).
|
||||||
|
|
||||||
|
If the insertion fails, we also try the same procedure for a flipped image.
|
||||||
|
|
||||||
|
### Additional heuristics
|
||||||
|
|
||||||
|
Now we know how to insert individual images into a bin of a given initial size S.
|
||||||
|
|
||||||
|
1. However, what S should be passed to the algorithm so that the rectangles end up wasting the least amount of space?
|
||||||
|
- We perform a binary search.
|
||||||
|
- We start with the size specified by the library user. Typically, it would be the maximum texture size allowed on a particular GPU.
|
||||||
|
- If the packing was successful on the given bin size, decrease the size and try to pack again.
|
||||||
|
- If the packing has failed on the given bin size - because some rectangles could not be further inserted - increase the size and try to pack again.
|
||||||
|
- The search is aborted if we've successfully inserted into a bin and the dimensions of the next candidate would differ from the previous by less than ``discard_step``.
|
||||||
|
- This variable exists so that we may easily trade accuracy for a speedup. ``discard_step = 1`` yields the highest accuracy. ``discard_step = 128`` will yield worse packings, but will be a lot faster, etc.
|
||||||
|
- The search is performed first by decreasing the bin size by both width and height, keeping it in square shape.
|
||||||
|
- Then we do the same, but only decreasing width.
|
||||||
|
- Then we do the same, but only decreasing height.
|
||||||
|
- The last two were a breakthrough in packing tightness. It turns out important to consider non-square bins.
|
||||||
|
2. In what order should the rectangles be inserted so that they pack the tightest?
|
||||||
|
- By default, the library tries 5 decreasing orders:
|
||||||
|
- By area.
|
||||||
|
- By perimeter.
|
||||||
|
- By the bigger side.
|
||||||
|
- By width.
|
||||||
|
- By height.
|
286
external/rectpack2D/best_bin_finder.h
vendored
Normal file
286
external/rectpack2D/best_bin_finder.h
vendored
Normal file
@ -0,0 +1,286 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <variant>
|
||||||
|
#include <cassert>
|
||||||
|
#include "rect_structs.h"
|
||||||
|
|
||||||
|
namespace rectpack2D {
|
||||||
|
enum class callback_result {
|
||||||
|
ABORT_PACKING,
|
||||||
|
CONTINUE_PACKING
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
auto& dereference(T& r) {
|
||||||
|
/*
|
||||||
|
This will allow us to pass orderings that consist of pointers,
|
||||||
|
as well as ones that are just plain objects in a vector.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if constexpr(std::is_pointer_v<T>) {
|
||||||
|
return *r;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
This function will do a binary search on viable bin sizes,
|
||||||
|
starting from the biggest one: starting_bin.
|
||||||
|
|
||||||
|
The search stops when the bin was successfully inserted into,
|
||||||
|
AND the bin size to be tried next differs in size from the last viable one by *less* then discard_step.
|
||||||
|
|
||||||
|
If we could not insert all input rectangles into a bin even as big as the starting_bin - the search fails.
|
||||||
|
In this case, we return the amount of space (total_area_type) inserted in total.
|
||||||
|
|
||||||
|
If we've found a viable bin that is smaller or equal to starting_bin, the search succeeds.
|
||||||
|
In this case, we return the viable bin (rect_wh).
|
||||||
|
*/
|
||||||
|
|
||||||
|
enum class bin_dimension {
|
||||||
|
BOTH,
|
||||||
|
WIDTH,
|
||||||
|
HEIGHT
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class empty_spaces_type, class O>
|
||||||
|
std::variant<total_area_type, rect_wh> best_packing_for_ordering_impl(
|
||||||
|
empty_spaces_type& root,
|
||||||
|
O ordering,
|
||||||
|
const rect_wh starting_bin,
|
||||||
|
int discard_step,
|
||||||
|
const bin_dimension tried_dimension
|
||||||
|
) {
|
||||||
|
auto candidate_bin = starting_bin;
|
||||||
|
int tries_before_discarding = 0;
|
||||||
|
|
||||||
|
if (discard_step <= 0)
|
||||||
|
{
|
||||||
|
tries_before_discarding = -discard_step;
|
||||||
|
discard_step = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//std::cout << "best_packing_for_ordering_impl dim: " << int(tried_dimension) << " w: " << starting_bin.w << " h: " << starting_bin.h << std::endl;
|
||||||
|
|
||||||
|
int starting_step = 0;
|
||||||
|
|
||||||
|
if (tried_dimension == bin_dimension::BOTH) {
|
||||||
|
candidate_bin.w /= 2;
|
||||||
|
candidate_bin.h /= 2;
|
||||||
|
|
||||||
|
starting_step = candidate_bin.w / 2;
|
||||||
|
}
|
||||||
|
else if (tried_dimension == bin_dimension::WIDTH) {
|
||||||
|
candidate_bin.w /= 2;
|
||||||
|
starting_step = candidate_bin.w / 2;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
candidate_bin.h /= 2;
|
||||||
|
starting_step = candidate_bin.h / 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int step = starting_step; ; step = std::max(1, step / 2)) {
|
||||||
|
//std::cout << "candidate: " << candidate_bin.w << "x" << candidate_bin.h << std::endl;
|
||||||
|
|
||||||
|
root.reset(candidate_bin);
|
||||||
|
|
||||||
|
int total_inserted_area = 0;
|
||||||
|
|
||||||
|
const bool all_inserted = [&]() {
|
||||||
|
for (const auto& r : ordering) {
|
||||||
|
const auto& rect = dereference(r).get_rect();
|
||||||
|
|
||||||
|
if (root.insert(rect.get_wh())) {
|
||||||
|
total_inserted_area += rect.area();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}();
|
||||||
|
|
||||||
|
if (all_inserted) {
|
||||||
|
/* Attempt was successful. Try with a smaller bin. */
|
||||||
|
|
||||||
|
if (step <= discard_step) {
|
||||||
|
if (tries_before_discarding > 0)
|
||||||
|
{
|
||||||
|
tries_before_discarding--;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return candidate_bin;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tried_dimension == bin_dimension::BOTH) {
|
||||||
|
candidate_bin.w -= step;
|
||||||
|
candidate_bin.h -= step;
|
||||||
|
}
|
||||||
|
else if (tried_dimension == bin_dimension::WIDTH) {
|
||||||
|
candidate_bin.w -= step;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
candidate_bin.h -= step;
|
||||||
|
}
|
||||||
|
|
||||||
|
root.reset(candidate_bin);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/* Attempt ended with failure. Try with a bigger bin. */
|
||||||
|
|
||||||
|
if (tried_dimension == bin_dimension::BOTH) {
|
||||||
|
candidate_bin.w += step;
|
||||||
|
candidate_bin.h += step;
|
||||||
|
|
||||||
|
if (candidate_bin.area() > starting_bin.area()) {
|
||||||
|
return total_inserted_area;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (tried_dimension == bin_dimension::WIDTH) {
|
||||||
|
candidate_bin.w += step;
|
||||||
|
|
||||||
|
if (candidate_bin.w > starting_bin.w) {
|
||||||
|
return total_inserted_area;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
candidate_bin.h += step;
|
||||||
|
|
||||||
|
if (candidate_bin.h > starting_bin.h) {
|
||||||
|
return total_inserted_area;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class empty_spaces_type, class O>
|
||||||
|
std::variant<total_area_type, rect_wh> best_packing_for_ordering(
|
||||||
|
empty_spaces_type& root,
|
||||||
|
O&& ordering,
|
||||||
|
const rect_wh starting_bin,
|
||||||
|
const int discard_step
|
||||||
|
) {
|
||||||
|
const auto try_pack = [&](
|
||||||
|
const bin_dimension tried_dimension,
|
||||||
|
const rect_wh starting_bin
|
||||||
|
) {
|
||||||
|
return best_packing_for_ordering_impl(
|
||||||
|
root,
|
||||||
|
std::forward<O>(ordering),
|
||||||
|
starting_bin,
|
||||||
|
discard_step,
|
||||||
|
tried_dimension
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto best_result = try_pack(bin_dimension::BOTH, starting_bin);
|
||||||
|
|
||||||
|
if (const auto failed = std::get_if<total_area_type>(&best_result)) {
|
||||||
|
return *failed;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto best_bin = std::get<rect_wh>(best_result);
|
||||||
|
|
||||||
|
auto trial = [&](const bin_dimension tried_dimension) {
|
||||||
|
const auto trial = try_pack(tried_dimension, best_bin);
|
||||||
|
|
||||||
|
if (const auto better = std::get_if<rect_wh>(&trial)) {
|
||||||
|
best_bin = *better;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
trial(bin_dimension::WIDTH);
|
||||||
|
trial(bin_dimension::HEIGHT);
|
||||||
|
|
||||||
|
return best_bin;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
This function will try to find the best bin size among the ones generated by all provided rectangle orders.
|
||||||
|
Only the best order will have results written to.
|
||||||
|
|
||||||
|
The function reports which of the rectangles did and did not fit in the end.
|
||||||
|
*/
|
||||||
|
|
||||||
|
template <
|
||||||
|
class empty_spaces_type,
|
||||||
|
class OrderType,
|
||||||
|
class F,
|
||||||
|
class I
|
||||||
|
>
|
||||||
|
rect_wh find_best_packing_impl(F for_each_order, const I input) {
|
||||||
|
const auto max_bin = rect_wh(input.max_bin_side, input.max_bin_side);
|
||||||
|
|
||||||
|
OrderType* best_order = nullptr;
|
||||||
|
|
||||||
|
int best_total_inserted = -1;
|
||||||
|
auto best_bin = max_bin;
|
||||||
|
|
||||||
|
/*
|
||||||
|
The root node is re-used on the TLS.
|
||||||
|
It is always reset before any packing attempt.
|
||||||
|
*/
|
||||||
|
|
||||||
|
thread_local empty_spaces_type root = rect_wh();
|
||||||
|
root.flipping_mode = input.flipping_mode;
|
||||||
|
|
||||||
|
for_each_order ([&](OrderType& current_order) {
|
||||||
|
const auto packing = best_packing_for_ordering(
|
||||||
|
root,
|
||||||
|
current_order,
|
||||||
|
max_bin,
|
||||||
|
input.discard_step
|
||||||
|
);
|
||||||
|
|
||||||
|
if (const auto total_inserted = std::get_if<total_area_type>(&packing)) {
|
||||||
|
/*
|
||||||
|
Track which function inserts the most area in total,
|
||||||
|
just in case that all orders will fail to fit into the largest allowed bin.
|
||||||
|
*/
|
||||||
|
if (best_order == nullptr) {
|
||||||
|
if (*total_inserted > best_total_inserted) {
|
||||||
|
best_order = std::addressof(current_order);
|
||||||
|
best_total_inserted = *total_inserted;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (const auto result_bin = std::get_if<rect_wh>(&packing)) {
|
||||||
|
/* Save the function if it performed the best. */
|
||||||
|
if (result_bin->area() <= best_bin.area()) {
|
||||||
|
best_order = std::addressof(current_order);
|
||||||
|
best_bin = *result_bin;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
{
|
||||||
|
assert(best_order != nullptr);
|
||||||
|
|
||||||
|
root.reset(best_bin);
|
||||||
|
|
||||||
|
for (auto& rr : *best_order) {
|
||||||
|
auto& rect = dereference(rr).get_rect();
|
||||||
|
|
||||||
|
if (const auto ret = root.insert(rect.get_wh())) {
|
||||||
|
rect = *ret;
|
||||||
|
|
||||||
|
if (callback_result::ABORT_PACKING == input.handle_successful_insertion(rect)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (callback_result::ABORT_PACKING == input.handle_unsuccessful_insertion(rect)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return root.get_rects_aabb();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
70
external/rectpack2D/empty_space_allocators.h
vendored
Normal file
70
external/rectpack2D/empty_space_allocators.h
vendored
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <array>
|
||||||
|
#include <vector>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
#include "rect_structs.h"
|
||||||
|
|
||||||
|
namespace rectpack2D {
|
||||||
|
class default_empty_spaces {
|
||||||
|
std::vector<space_rect> empty_spaces;
|
||||||
|
|
||||||
|
public:
|
||||||
|
void remove(const int i) {
|
||||||
|
empty_spaces[i] = empty_spaces.back();
|
||||||
|
empty_spaces.pop_back();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool add(const space_rect r) {
|
||||||
|
empty_spaces.emplace_back(r);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto get_count() const {
|
||||||
|
return empty_spaces.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
void reset() {
|
||||||
|
empty_spaces.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto& get(const int i) {
|
||||||
|
return empty_spaces[i];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <int MAX_SPACES>
|
||||||
|
class static_empty_spaces {
|
||||||
|
int count_spaces = 0;
|
||||||
|
std::array<space_rect, MAX_SPACES> empty_spaces;
|
||||||
|
|
||||||
|
public:
|
||||||
|
void remove(const int i) {
|
||||||
|
empty_spaces[i] = empty_spaces[count_spaces - 1];
|
||||||
|
--count_spaces;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool add(const space_rect r) {
|
||||||
|
if (count_spaces < static_cast<int>(empty_spaces.size())) {
|
||||||
|
empty_spaces[count_spaces] = r;
|
||||||
|
++count_spaces;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto get_count() const {
|
||||||
|
return count_spaces;
|
||||||
|
}
|
||||||
|
|
||||||
|
void reset() {
|
||||||
|
count_spaces = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto& get(const int i) {
|
||||||
|
return empty_spaces[i];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
149
external/rectpack2D/empty_spaces.h
vendored
Normal file
149
external/rectpack2D/empty_spaces.h
vendored
Normal file
@ -0,0 +1,149 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "insert_and_split.h"
|
||||||
|
|
||||||
|
namespace rectpack2D {
|
||||||
|
enum class flipping_option {
|
||||||
|
DISABLED,
|
||||||
|
ENABLED
|
||||||
|
};
|
||||||
|
|
||||||
|
class default_empty_spaces;
|
||||||
|
|
||||||
|
template <bool allow_flip, class empty_spaces_provider = default_empty_spaces>
|
||||||
|
class empty_spaces {
|
||||||
|
rect_wh current_aabb;
|
||||||
|
empty_spaces_provider spaces;
|
||||||
|
|
||||||
|
/* MSVC fix for non-conformant if constexpr implementation */
|
||||||
|
|
||||||
|
static auto make_output_rect(const int x, const int y, const int w, const int h) {
|
||||||
|
return rect_xywh(x, y, w, h);
|
||||||
|
}
|
||||||
|
|
||||||
|
static auto make_output_rect(const int x, const int y, const int w, const int h, const bool flipped) {
|
||||||
|
return rect_xywhf(x, y, w, h, flipped);
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
using output_rect_type = std::conditional_t<allow_flip, rect_xywhf, rect_xywh>;
|
||||||
|
|
||||||
|
flipping_option flipping_mode = flipping_option::ENABLED;
|
||||||
|
|
||||||
|
empty_spaces(const rect_wh& r) {
|
||||||
|
reset(r);
|
||||||
|
}
|
||||||
|
|
||||||
|
void reset(const rect_wh& r) {
|
||||||
|
current_aabb = {};
|
||||||
|
|
||||||
|
spaces.reset();
|
||||||
|
spaces.add(rect_xywh(0, 0, r.w, r.h));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class F>
|
||||||
|
std::optional<output_rect_type> insert(const rect_wh image_rectangle, F report_candidate_empty_space) {
|
||||||
|
for (int i = static_cast<int>(spaces.get_count()) - 1; i >= 0; --i) {
|
||||||
|
const auto candidate_space = spaces.get(i);
|
||||||
|
|
||||||
|
report_candidate_empty_space(candidate_space);
|
||||||
|
|
||||||
|
auto accept_result = [this, i, image_rectangle, candidate_space](
|
||||||
|
const created_splits& splits,
|
||||||
|
const bool flipping_necessary
|
||||||
|
) -> std::optional<output_rect_type> {
|
||||||
|
spaces.remove(i);
|
||||||
|
|
||||||
|
for (int s = 0; s < splits.count; ++s) {
|
||||||
|
if (!spaces.add(splits.spaces[s])) {
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if constexpr(allow_flip) {
|
||||||
|
const auto result = make_output_rect(
|
||||||
|
candidate_space.x,
|
||||||
|
candidate_space.y,
|
||||||
|
image_rectangle.w,
|
||||||
|
image_rectangle.h,
|
||||||
|
flipping_necessary
|
||||||
|
);
|
||||||
|
|
||||||
|
current_aabb.expand_with(result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
else if constexpr(!allow_flip) {
|
||||||
|
(void)flipping_necessary;
|
||||||
|
|
||||||
|
const auto result = make_output_rect(
|
||||||
|
candidate_space.x,
|
||||||
|
candidate_space.y,
|
||||||
|
image_rectangle.w,
|
||||||
|
image_rectangle.h
|
||||||
|
);
|
||||||
|
|
||||||
|
current_aabb.expand_with(result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
auto try_to_insert = [&](const rect_wh& img) {
|
||||||
|
return rectpack2D::insert_and_split(img, candidate_space);
|
||||||
|
};
|
||||||
|
|
||||||
|
if constexpr(!allow_flip) {
|
||||||
|
if (const auto normal = try_to_insert(image_rectangle)) {
|
||||||
|
return accept_result(normal, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (flipping_mode == flipping_option::ENABLED) {
|
||||||
|
const auto normal = try_to_insert(image_rectangle);
|
||||||
|
const auto flipped = try_to_insert(rect_wh(image_rectangle).flip());
|
||||||
|
|
||||||
|
/*
|
||||||
|
If both were successful,
|
||||||
|
prefer the one that generated less remainder spaces.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (normal && flipped) {
|
||||||
|
if (flipped.better_than(normal)) {
|
||||||
|
/* Accept the flipped result if it producues less or "better" spaces. */
|
||||||
|
|
||||||
|
return accept_result(flipped, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return accept_result(normal, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (normal) {
|
||||||
|
return accept_result(normal, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flipped) {
|
||||||
|
return accept_result(flipped, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (const auto normal = try_to_insert(image_rectangle)) {
|
||||||
|
return accept_result(normal, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
decltype(auto) insert(const rect_wh& image_rectangle) {
|
||||||
|
return insert(image_rectangle, [](auto&){ });
|
||||||
|
}
|
||||||
|
|
||||||
|
auto get_rects_aabb() const {
|
||||||
|
return current_aabb;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto& get_spaces() const {
|
||||||
|
return spaces;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
154
external/rectpack2D/finders_interface.h
vendored
Normal file
154
external/rectpack2D/finders_interface.h
vendored
Normal file
@ -0,0 +1,154 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <optional>
|
||||||
|
#include <vector>
|
||||||
|
#include <array>
|
||||||
|
#include <variant>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
#include "insert_and_split.h"
|
||||||
|
#include "empty_spaces.h"
|
||||||
|
#include "empty_space_allocators.h"
|
||||||
|
|
||||||
|
#include "best_bin_finder.h"
|
||||||
|
|
||||||
|
namespace rectpack2D {
|
||||||
|
template <class empty_spaces_type>
|
||||||
|
using output_rect_t = typename empty_spaces_type::output_rect_type;
|
||||||
|
|
||||||
|
template <class F, class G>
|
||||||
|
struct finder_input {
|
||||||
|
const int max_bin_side;
|
||||||
|
const int discard_step;
|
||||||
|
F handle_successful_insertion;
|
||||||
|
G handle_unsuccessful_insertion;
|
||||||
|
const flipping_option flipping_mode;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class F, class G>
|
||||||
|
auto make_finder_input(
|
||||||
|
const int max_bin_side,
|
||||||
|
const int discard_step,
|
||||||
|
F&& handle_successful_insertion,
|
||||||
|
G&& handle_unsuccessful_insertion,
|
||||||
|
const flipping_option flipping_mode
|
||||||
|
) {
|
||||||
|
return finder_input<F, G> {
|
||||||
|
max_bin_side,
|
||||||
|
discard_step,
|
||||||
|
std::forward<F>(handle_successful_insertion),
|
||||||
|
std::forward<G>(handle_unsuccessful_insertion),
|
||||||
|
flipping_mode
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
Finds the best packing for the rectangles,
|
||||||
|
just in the order that they were passed.
|
||||||
|
*/
|
||||||
|
|
||||||
|
template <class empty_spaces_type, class Subjects, class F, class G>
|
||||||
|
rect_wh find_best_packing_dont_sort(
|
||||||
|
Subjects& subjects,
|
||||||
|
const finder_input<F, G>& input
|
||||||
|
) {
|
||||||
|
using order_type = std::remove_reference_t<decltype(subjects)>;
|
||||||
|
|
||||||
|
return find_best_packing_impl<empty_spaces_type, order_type>(
|
||||||
|
[&subjects](auto callback) { callback(subjects); },
|
||||||
|
input
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
Finds the best packing for the rectangles.
|
||||||
|
Accepts a list of predicates able to compare two input rectangles.
|
||||||
|
|
||||||
|
The function will try to pack the rectangles in all orders generated by the predicates,
|
||||||
|
and will only write the x, y coordinates of the best packing found among the orders.
|
||||||
|
*/
|
||||||
|
|
||||||
|
template <class empty_spaces_type, class Subjects, class F, class G, class Comparator, class... Comparators>
|
||||||
|
rect_wh find_best_packing(
|
||||||
|
Subjects& subjects,
|
||||||
|
const finder_input<F, G>& input,
|
||||||
|
|
||||||
|
Comparator comparator,
|
||||||
|
Comparators... comparators
|
||||||
|
) {
|
||||||
|
using rect_type = output_rect_t<empty_spaces_type>;
|
||||||
|
using order_type = std::vector<rect_type*>;
|
||||||
|
|
||||||
|
constexpr auto count_orders = 1 + sizeof...(Comparators);
|
||||||
|
thread_local std::array<order_type, count_orders> orders;
|
||||||
|
|
||||||
|
{
|
||||||
|
/* order[0] will always exist since this overload requires at least one comparator */
|
||||||
|
auto& initial_pointers = orders[0];
|
||||||
|
initial_pointers.clear();
|
||||||
|
|
||||||
|
for (auto& s : subjects) {
|
||||||
|
auto& r = s.get_rect();
|
||||||
|
|
||||||
|
if (r.area() > 0) {
|
||||||
|
initial_pointers.emplace_back(std::addressof(r));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (std::size_t i = 1; i < count_orders; ++i) {
|
||||||
|
orders[i] = initial_pointers;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t f = 0;
|
||||||
|
|
||||||
|
auto& orders_ref = orders;
|
||||||
|
|
||||||
|
auto make_order = [&f, &orders_ref](auto& predicate) {
|
||||||
|
std::sort(orders_ref[f].begin(), orders_ref[f].end(), predicate);
|
||||||
|
++f;
|
||||||
|
};
|
||||||
|
|
||||||
|
make_order(comparator);
|
||||||
|
(make_order(comparators), ...);
|
||||||
|
|
||||||
|
return find_best_packing_impl<empty_spaces_type, order_type>(
|
||||||
|
[&orders_ref](auto callback){ for (auto& o : orders_ref) { callback(o); } },
|
||||||
|
input
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Finds the best packing for the rectangles.
|
||||||
|
Provides a list of several sensible comparison predicates.
|
||||||
|
*/
|
||||||
|
|
||||||
|
template <class empty_spaces_type, class Subjects, class F, class G>
|
||||||
|
rect_wh find_best_packing(
|
||||||
|
Subjects& subjects,
|
||||||
|
const finder_input<F, G>& input
|
||||||
|
) {
|
||||||
|
using rect_type = output_rect_t<empty_spaces_type>;
|
||||||
|
|
||||||
|
return find_best_packing<empty_spaces_type>(
|
||||||
|
subjects,
|
||||||
|
input,
|
||||||
|
|
||||||
|
[](const rect_type* const a, const rect_type* const b) {
|
||||||
|
return a->area() > b->area();
|
||||||
|
},
|
||||||
|
[](const rect_type* const a, const rect_type* const b) {
|
||||||
|
return a->perimeter() > b->perimeter();
|
||||||
|
},
|
||||||
|
[](const rect_type* const a, const rect_type* const b) {
|
||||||
|
return std::max(a->w, a->h) > std::max(b->w, b->h);
|
||||||
|
},
|
||||||
|
[](const rect_type* const a, const rect_type* const b) {
|
||||||
|
return a->w > b->w;
|
||||||
|
},
|
||||||
|
[](const rect_type* const a, const rect_type* const b) {
|
||||||
|
return a->h > b->h;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
135
external/rectpack2D/insert_and_split.h
vendored
Normal file
135
external/rectpack2D/insert_and_split.h
vendored
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <array>
|
||||||
|
#include "rect_structs.h"
|
||||||
|
|
||||||
|
namespace rectpack2D {
|
||||||
|
struct created_splits {
|
||||||
|
int count = 0;
|
||||||
|
std::array<space_rect, 2> spaces;
|
||||||
|
|
||||||
|
static auto failed() {
|
||||||
|
created_splits result;
|
||||||
|
result.count = -1;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static auto none() {
|
||||||
|
return created_splits();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class... Args>
|
||||||
|
created_splits(Args&&... args) : spaces({ std::forward<Args>(args)... }) {
|
||||||
|
count = sizeof...(Args);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool better_than(const created_splits& b) const {
|
||||||
|
return count < b.count;
|
||||||
|
}
|
||||||
|
|
||||||
|
explicit operator bool() const {
|
||||||
|
return count != -1;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
inline created_splits insert_and_split(
|
||||||
|
const rect_wh& im, /* Image rectangle */
|
||||||
|
const space_rect& sp /* Space rectangle */
|
||||||
|
) {
|
||||||
|
const auto free_w = sp.w - im.w;
|
||||||
|
const auto free_h = sp.h - im.h;
|
||||||
|
|
||||||
|
if (free_w < 0 || free_h < 0) {
|
||||||
|
/*
|
||||||
|
Image is bigger than the candidate empty space.
|
||||||
|
We'll need to look further.
|
||||||
|
*/
|
||||||
|
|
||||||
|
return created_splits::failed();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (free_w == 0 && free_h == 0) {
|
||||||
|
/*
|
||||||
|
If the image dimensions equal the dimensions of the candidate empty space (image fits exactly),
|
||||||
|
we will just delete the space and create no splits.
|
||||||
|
*/
|
||||||
|
|
||||||
|
return created_splits::none();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
If the image fits into the candidate empty space,
|
||||||
|
but exactly one of the image dimensions equals the respective dimension of the candidate empty space
|
||||||
|
(e.g. image = 20x40, candidate space = 30x40)
|
||||||
|
we delete the space and create a single split. In this case a 10x40 space.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (free_w > 0 && free_h == 0) {
|
||||||
|
auto r = sp;
|
||||||
|
r.x += im.w;
|
||||||
|
r.w -= im.w;
|
||||||
|
return created_splits(r);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (free_w == 0 && free_h > 0) {
|
||||||
|
auto r = sp;
|
||||||
|
r.y += im.h;
|
||||||
|
r.h -= im.h;
|
||||||
|
return created_splits(r);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Every other option has been exhausted,
|
||||||
|
so at this point the image must be *strictly* smaller than the empty space,
|
||||||
|
that is, it is smaller in both width and height.
|
||||||
|
|
||||||
|
Thus, free_w and free_h must be positive.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
Decide which way to split.
|
||||||
|
|
||||||
|
Instead of having two normally-sized spaces,
|
||||||
|
it is better - though I have no proof of that - to have a one tiny space and a one huge space.
|
||||||
|
This creates better opportunity for insertion of future rectangles.
|
||||||
|
|
||||||
|
This is why, if we had more of width remaining than we had of height,
|
||||||
|
we split along the vertical axis,
|
||||||
|
and if we had more of height remaining than we had of width,
|
||||||
|
we split along the horizontal axis.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (free_w > free_h) {
|
||||||
|
const auto bigger_split = space_rect(
|
||||||
|
sp.x + im.w,
|
||||||
|
sp.y,
|
||||||
|
free_w,
|
||||||
|
sp.h
|
||||||
|
);
|
||||||
|
|
||||||
|
const auto lesser_split = space_rect(
|
||||||
|
sp.x,
|
||||||
|
sp.y + im.h,
|
||||||
|
im.w,
|
||||||
|
free_h
|
||||||
|
);
|
||||||
|
|
||||||
|
return created_splits(bigger_split, lesser_split);
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto bigger_split = space_rect(
|
||||||
|
sp.x,
|
||||||
|
sp.y + im.h,
|
||||||
|
sp.w,
|
||||||
|
free_h
|
||||||
|
);
|
||||||
|
|
||||||
|
const auto lesser_split = space_rect(
|
||||||
|
sp.x + im.w,
|
||||||
|
sp.y,
|
||||||
|
free_w,
|
||||||
|
im.h
|
||||||
|
);
|
||||||
|
|
||||||
|
return created_splits(bigger_split, lesser_split);
|
||||||
|
}
|
||||||
|
}
|
90
external/rectpack2D/rect_structs.h
vendored
Normal file
90
external/rectpack2D/rect_structs.h
vendored
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
namespace rectpack2D {
|
||||||
|
using total_area_type = int;
|
||||||
|
|
||||||
|
struct rect_wh {
|
||||||
|
rect_wh() : w(0), h(0) {}
|
||||||
|
rect_wh(const int w, const int h) : w(w), h(h) {}
|
||||||
|
|
||||||
|
int w;
|
||||||
|
int h;
|
||||||
|
|
||||||
|
auto& flip() {
|
||||||
|
std::swap(w, h);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
int max_side() const {
|
||||||
|
return h > w ? h : w;
|
||||||
|
}
|
||||||
|
|
||||||
|
int min_side() const {
|
||||||
|
return h < w ? h : w;
|
||||||
|
}
|
||||||
|
|
||||||
|
int area() const { return w * h; }
|
||||||
|
int perimeter() const { return 2 * w + 2 * h; }
|
||||||
|
|
||||||
|
template <class R>
|
||||||
|
void expand_with(const R& r) {
|
||||||
|
w = std::max(w, r.x + r.w);
|
||||||
|
h = std::max(h, r.y + r.h);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct rect_xywh {
|
||||||
|
int x;
|
||||||
|
int y;
|
||||||
|
int w;
|
||||||
|
int h;
|
||||||
|
|
||||||
|
rect_xywh() : x(0), y(0), w(0), h(0) {}
|
||||||
|
rect_xywh(const int x, const int y, const int w, const int h) : x(x), y(y), w(w), h(h) {}
|
||||||
|
|
||||||
|
int area() const { return w * h; }
|
||||||
|
int perimeter() const { return 2 * w + 2 * h; }
|
||||||
|
|
||||||
|
auto get_wh() const {
|
||||||
|
return rect_wh(w, h);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto& get_rect() {
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto& get_rect() const {
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct rect_xywhf {
|
||||||
|
int x;
|
||||||
|
int y;
|
||||||
|
int w;
|
||||||
|
int h;
|
||||||
|
bool flipped;
|
||||||
|
|
||||||
|
rect_xywhf() : x(0), y(0), w(0), h(0), flipped(false) {}
|
||||||
|
rect_xywhf(const int x, const int y, const int w, const int h, const bool flipped) : x(x), y(y), w(flipped ? h : w), h(flipped ? w : h), flipped(flipped) {}
|
||||||
|
rect_xywhf(const rect_xywh& b) : rect_xywhf(b.x, b.y, b.w, b.h, false) {}
|
||||||
|
|
||||||
|
int area() const { return w * h; }
|
||||||
|
int perimeter() const { return 2 * w + 2 * h; }
|
||||||
|
|
||||||
|
auto get_wh() const {
|
||||||
|
return rect_wh(w, h);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto& get_rect() {
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto& get_rect() const {
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
using space_rect = rect_xywh;
|
||||||
|
}
|
@ -40,7 +40,7 @@ private:
|
|||||||
public:
|
public:
|
||||||
|
|
||||||
LeafQuery();
|
LeafQuery();
|
||||||
~LeafQuery() { }
|
~LeafQuery();
|
||||||
void getNextEntry( const SpatialIndex::IEntry& entry,
|
void getNextEntry( const SpatialIndex::IEntry& entry,
|
||||||
SpatialIndex::id_type& nextEntry,
|
SpatialIndex::id_type& nextEntry,
|
||||||
bool& hasNext);
|
bool& hasNext);
|
||||||
|
2
external/spatialindex/src/capi/LeafQuery.cc
vendored
2
external/spatialindex/src/capi/LeafQuery.cc
vendored
@ -33,6 +33,8 @@ LeafQuery::LeafQuery()
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LeafQuery::~LeafQuery() = default;
|
||||||
|
|
||||||
LeafQueryResult get_results(const SpatialIndex::INode* n)
|
LeafQueryResult get_results(const SpatialIndex::INode* n)
|
||||||
{
|
{
|
||||||
LeafQueryResult result (n->getIdentifier());
|
LeafQueryResult result (n->getIdentifier());
|
||||||
|
2
external/spatialindex/src/capi/sidx_api.cc
vendored
2
external/spatialindex/src/capi/sidx_api.cc
vendored
@ -47,7 +47,7 @@ static __thread struct
|
|||||||
int code;
|
int code;
|
||||||
char message[LAST_ERROR_BUFFER_SIZE];
|
char message[LAST_ERROR_BUFFER_SIZE];
|
||||||
char method[LAST_ERROR_BUFFER_SIZE];
|
char method[LAST_ERROR_BUFFER_SIZE];
|
||||||
} last_error = {0};
|
} last_error = {0, "", ""};
|
||||||
#else
|
#else
|
||||||
static std::stack<Error> errors;
|
static std::stack<Error> errors;
|
||||||
#endif
|
#endif
|
||||||
|
2
external/spatialindex/src/mvrtree/MVRTree.cc
vendored
2
external/spatialindex/src/mvrtree/MVRTree.cc
vendored
@ -548,7 +548,7 @@ bool SpatialIndex::MVRTree::MVRTree::isIndexValid()
|
|||||||
|
|
||||||
//std::cerr << "Total accessible nodes: " << visitedEntries.size() << std::endl;
|
//std::cerr << "Total accessible nodes: " << visitedEntries.size() << std::endl;
|
||||||
//std::cerr << "Degenerate nodes: " << degenerateEntries << std::endl;
|
//std::cerr << "Degenerate nodes: " << degenerateEntries << std::endl;
|
||||||
|
(void)degenerateEntries;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,6 +45,7 @@ namespace Tools
|
|||||||
while (! m_pool.empty())
|
while (! m_pool.empty())
|
||||||
{
|
{
|
||||||
SpatialIndex::MVRTree::Node* x = m_pool.top(); m_pool.pop();
|
SpatialIndex::MVRTree::Node* x = m_pool.top(); m_pool.pop();
|
||||||
|
(void)x;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
120
images/themes/default/mIconEsriI3s.svg
Normal file
120
images/themes/default/mIconEsriI3s.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 14 KiB |
@ -6,6 +6,7 @@
|
|||||||
%Include auto_generated/qgs3dtypes.sip
|
%Include auto_generated/qgs3dtypes.sip
|
||||||
%Include auto_generated/qgs3dmapcanvas.sip
|
%Include auto_generated/qgs3dmapcanvas.sip
|
||||||
%Include auto_generated/qgsabstractvectorlayer3drenderer.sip
|
%Include auto_generated/qgsabstractvectorlayer3drenderer.sip
|
||||||
|
%Include auto_generated/qgsannotationlayer3drenderer.sip
|
||||||
%Include auto_generated/qgscameracontroller.sip
|
%Include auto_generated/qgscameracontroller.sip
|
||||||
%Include auto_generated/qgscamerapose.sip
|
%Include auto_generated/qgscamerapose.sip
|
||||||
%Include auto_generated/qgslayoutitem3dmap.sip
|
%Include auto_generated/qgslayoutitem3dmap.sip
|
||||||
@ -13,6 +14,7 @@
|
|||||||
%Include auto_generated/qgstiledscenelayer3drenderer.sip
|
%Include auto_generated/qgstiledscenelayer3drenderer.sip
|
||||||
%Include auto_generated/qgsvectorlayer3drenderer.sip
|
%Include auto_generated/qgsvectorlayer3drenderer.sip
|
||||||
%Include auto_generated/qgspointcloudlayer3drenderer.sip
|
%Include auto_generated/qgspointcloudlayer3drenderer.sip
|
||||||
|
%Include auto_generated/qgstextureatlasgenerator.sip
|
||||||
%Include auto_generated/lights/qgsdirectionallightsettings.sip
|
%Include auto_generated/lights/qgsdirectionallightsettings.sip
|
||||||
%Include auto_generated/lights/qgslightsource.sip
|
%Include auto_generated/lights/qgslightsource.sip
|
||||||
%Include auto_generated/lights/qgspointlightsettings.sip
|
%Include auto_generated/lights/qgspointlightsettings.sip
|
||||||
|
9
python/3d/auto_additions/qgsannotationlayer3drenderer.py
Normal file
9
python/3d/auto_additions/qgsannotationlayer3drenderer.py
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
# The following has been generated automatically from src/3d/qgsannotationlayer3drenderer.h
|
||||||
|
try:
|
||||||
|
QgsAnnotationLayer3DRendererMetadata.__overridden_methods__ = ['createRenderer']
|
||||||
|
except (NameError, AttributeError):
|
||||||
|
pass
|
||||||
|
try:
|
||||||
|
QgsAnnotationLayer3DRenderer.__overridden_methods__ = ['type', 'clone', 'writeXml', 'readXml', 'resolveReferences']
|
||||||
|
except (NameError, AttributeError):
|
||||||
|
pass
|
6
python/3d/auto_additions/qgstextureatlasgenerator.py
Normal file
6
python/3d/auto_additions/qgstextureatlasgenerator.py
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
# The following has been generated automatically from src/3d/qgstextureatlasgenerator.h
|
||||||
|
try:
|
||||||
|
QgsTextureAtlasGenerator.createFromRects = staticmethod(QgsTextureAtlasGenerator.createFromRects)
|
||||||
|
QgsTextureAtlasGenerator.createFromImages = staticmethod(QgsTextureAtlasGenerator.createFromImages)
|
||||||
|
except (NameError, AttributeError):
|
||||||
|
pass
|
154
python/3d/auto_generated/qgsannotationlayer3drenderer.sip.in
Normal file
154
python/3d/auto_generated/qgsannotationlayer3drenderer.sip.in
Normal file
@ -0,0 +1,154 @@
|
|||||||
|
/************************************************************************
|
||||||
|
* This file has been generated automatically from *
|
||||||
|
* *
|
||||||
|
* src/3d/qgsannotationlayer3drenderer.h *
|
||||||
|
* *
|
||||||
|
* Do not edit manually ! Edit header and run scripts/sipify.py again *
|
||||||
|
************************************************************************/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// this is needed for the "convert to subclass" code below to compile
|
||||||
|
%ModuleHeaderCode
|
||||||
|
#include "qgsannotationlayer3drenderer.h"
|
||||||
|
%End
|
||||||
|
|
||||||
|
class QgsAnnotationLayer3DRendererMetadata : Qgs3DRendererAbstractMetadata
|
||||||
|
{
|
||||||
|
%Docstring(signature="appended")
|
||||||
|
Metadata for annotation layer 3D renderer to allow creation of its
|
||||||
|
instances from XML.
|
||||||
|
|
||||||
|
.. warning::
|
||||||
|
|
||||||
|
This is not considered stable API, and may change in future QGIS releases. It is
|
||||||
|
exposed to the Python bindings as a tech preview only.
|
||||||
|
|
||||||
|
.. versionadded:: 4.0
|
||||||
|
%End
|
||||||
|
|
||||||
|
%TypeHeaderCode
|
||||||
|
#include "qgsannotationlayer3drenderer.h"
|
||||||
|
%End
|
||||||
|
public:
|
||||||
|
QgsAnnotationLayer3DRendererMetadata();
|
||||||
|
|
||||||
|
virtual QgsAbstract3DRenderer *createRenderer( QDomElement &elem, const QgsReadWriteContext &context ) /Factory/;
|
||||||
|
|
||||||
|
%Docstring
|
||||||
|
Creates an instance of a 3D renderer based on a DOM element with
|
||||||
|
renderer configuration
|
||||||
|
%End
|
||||||
|
};
|
||||||
|
|
||||||
|
class QgsAnnotationLayer3DRenderer : QgsAbstract3DRenderer
|
||||||
|
{
|
||||||
|
%Docstring(signature="appended")
|
||||||
|
3D renderers for annotation layers.
|
||||||
|
|
||||||
|
.. versionadded:: 4.0
|
||||||
|
%End
|
||||||
|
|
||||||
|
%TypeHeaderCode
|
||||||
|
#include "qgsannotationlayer3drenderer.h"
|
||||||
|
%End
|
||||||
|
%ConvertToSubClassCode
|
||||||
|
if ( dynamic_cast<QgsAnnotationLayer3DRenderer *>( sipCpp ) != nullptr )
|
||||||
|
sipType = sipType_QgsAnnotationLayer3DRenderer;
|
||||||
|
else
|
||||||
|
sipType = nullptr;
|
||||||
|
%End
|
||||||
|
public:
|
||||||
|
QgsAnnotationLayer3DRenderer();
|
||||||
|
|
||||||
|
void setLayer( QgsAnnotationLayer *layer );
|
||||||
|
%Docstring
|
||||||
|
Sets the annotation layer associated with the renderer.
|
||||||
|
|
||||||
|
.. seealso:: :py:func:`layer`
|
||||||
|
%End
|
||||||
|
|
||||||
|
QgsAnnotationLayer *layer() const;
|
||||||
|
%Docstring
|
||||||
|
Returns the annotation layer associated with the renderer.
|
||||||
|
|
||||||
|
.. seealso:: :py:func:`setLayer`
|
||||||
|
%End
|
||||||
|
|
||||||
|
virtual QString type() const;
|
||||||
|
|
||||||
|
virtual QgsAnnotationLayer3DRenderer *clone() const /Factory/;
|
||||||
|
|
||||||
|
|
||||||
|
virtual void writeXml( QDomElement &elem, const QgsReadWriteContext &context ) const;
|
||||||
|
|
||||||
|
virtual void readXml( const QDomElement &elem, const QgsReadWriteContext &context );
|
||||||
|
|
||||||
|
virtual void resolveReferences( const QgsProject &project );
|
||||||
|
|
||||||
|
|
||||||
|
Qgis::AltitudeClamping altitudeClamping() const;
|
||||||
|
%Docstring
|
||||||
|
Returns the altitude clamping method, which determines the vertical
|
||||||
|
position of annotations.
|
||||||
|
|
||||||
|
.. seealso:: :py:func:`setAltitudeClamping`
|
||||||
|
%End
|
||||||
|
|
||||||
|
void setAltitudeClamping( Qgis::AltitudeClamping clamping );
|
||||||
|
%Docstring
|
||||||
|
Sets the altitude ``clamping`` method, which determines the vertical
|
||||||
|
position of annotations.
|
||||||
|
|
||||||
|
.. seealso:: :py:func:`altitudeClamping`
|
||||||
|
%End
|
||||||
|
|
||||||
|
double zOffset() const;
|
||||||
|
%Docstring
|
||||||
|
Returns the z offset, which is a fixed offset amount which should be
|
||||||
|
added to z values for the annotations.
|
||||||
|
|
||||||
|
.. seealso:: :py:func:`setZOffset`
|
||||||
|
%End
|
||||||
|
|
||||||
|
void setZOffset( double offset );
|
||||||
|
%Docstring
|
||||||
|
Sets the z ``offset``, which is a fixed offset amount which will be
|
||||||
|
added to z values for the annotations.
|
||||||
|
|
||||||
|
.. seealso:: :py:func:`zOffset`
|
||||||
|
%End
|
||||||
|
|
||||||
|
bool showCalloutLines() const;
|
||||||
|
%Docstring
|
||||||
|
Returns ``True`` if callout lines are shown, vertically joining the
|
||||||
|
annotations to the terrain.
|
||||||
|
|
||||||
|
.. seealso:: :py:func:`setShowCalloutLines`
|
||||||
|
%End
|
||||||
|
|
||||||
|
void setShowCalloutLines( bool show );
|
||||||
|
%Docstring
|
||||||
|
Sets whether callout lines are shown, vertically joining the annotations
|
||||||
|
to the terrain.
|
||||||
|
|
||||||
|
.. seealso:: :py:func:`showCalloutLines`
|
||||||
|
%End
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private:
|
||||||
|
QgsAnnotationLayer3DRenderer( const QgsAnnotationLayer3DRenderer & );
|
||||||
|
};
|
||||||
|
|
||||||
|
/************************************************************************
|
||||||
|
* This file has been generated automatically from *
|
||||||
|
* *
|
||||||
|
* src/3d/qgsannotationlayer3drenderer.h *
|
||||||
|
* *
|
||||||
|
* Do not edit manually ! Edit header and run scripts/sipify.py again *
|
||||||
|
************************************************************************/
|
154
python/3d/auto_generated/qgstextureatlasgenerator.sip.in
Normal file
154
python/3d/auto_generated/qgstextureatlasgenerator.sip.in
Normal file
@ -0,0 +1,154 @@
|
|||||||
|
/************************************************************************
|
||||||
|
* This file has been generated automatically from *
|
||||||
|
* *
|
||||||
|
* src/3d/qgstextureatlasgenerator.h *
|
||||||
|
* *
|
||||||
|
* Do not edit manually ! Edit header and run scripts/sipify.py again *
|
||||||
|
************************************************************************/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class QgsTextureAtlas
|
||||||
|
{
|
||||||
|
%Docstring(signature="appended")
|
||||||
|
Encapsulates a texture atlas.
|
||||||
|
|
||||||
|
:py:class:`QgsTextureAtlas` contains the packed regions for aggregated
|
||||||
|
texture atlases, and optionally the packed texture map.
|
||||||
|
|
||||||
|
See :py:class:`QgsTextureAtlasGenerator` for a class which automatically
|
||||||
|
creates texture atlases.
|
||||||
|
|
||||||
|
.. versionadded:: 4.0
|
||||||
|
%End
|
||||||
|
|
||||||
|
%TypeHeaderCode
|
||||||
|
#include "qgstextureatlasgenerator.h"
|
||||||
|
%End
|
||||||
|
public:
|
||||||
|
QgsTextureAtlas();
|
||||||
|
~QgsTextureAtlas();
|
||||||
|
|
||||||
|
QgsTextureAtlas( const QgsTextureAtlas &other );
|
||||||
|
|
||||||
|
bool isValid() const;
|
||||||
|
%Docstring
|
||||||
|
Returns ``True`` if the atlas is valid.
|
||||||
|
%End
|
||||||
|
|
||||||
|
QSize atlasSize() const;
|
||||||
|
%Docstring
|
||||||
|
Returns the total size required for the atlas, i.e. the total size for
|
||||||
|
the packed images and rectangles.
|
||||||
|
%End
|
||||||
|
|
||||||
|
QRect rect( int index ) const;
|
||||||
|
%Docstring
|
||||||
|
Returns the packed rectangle for the texture with the specified
|
||||||
|
``index``.
|
||||||
|
|
||||||
|
:raises IndexError: if no texture with the specified index exists.
|
||||||
|
%End
|
||||||
|
%MethodCode
|
||||||
|
const int count = sipCpp->count();
|
||||||
|
if ( a0 < 0 || a0 >= count )
|
||||||
|
{
|
||||||
|
PyErr_SetString( PyExc_IndexError, QByteArray::number( a0 ) );
|
||||||
|
sipIsErr = 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return sipConvertFromNewType( new QRect( sipCpp->rect( a0 ) ), sipType_QRect, Py_None );
|
||||||
|
}
|
||||||
|
%End
|
||||||
|
|
||||||
|
QImage renderAtlasTexture() const;
|
||||||
|
%Docstring
|
||||||
|
Renders the combined texture atlas, containing all source images.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
This may be a null image if the atlas was created with rectangles alone.
|
||||||
|
%End
|
||||||
|
|
||||||
|
QImage renderDebugTexture() const;
|
||||||
|
%Docstring
|
||||||
|
Renders a debug texture.
|
||||||
|
|
||||||
|
The debug texture renders all packed rectangles with a unique color, and
|
||||||
|
can be used to visualize the solution.
|
||||||
|
%End
|
||||||
|
|
||||||
|
int count() const;
|
||||||
|
%Docstring
|
||||||
|
Returns the number of textures in the atlas.
|
||||||
|
%End
|
||||||
|
|
||||||
|
int __len__() const;
|
||||||
|
%Docstring
|
||||||
|
Returns the number of textures in the atlas.
|
||||||
|
%End
|
||||||
|
%MethodCode
|
||||||
|
sipRes
|
||||||
|
= sipCpp->count();
|
||||||
|
%End
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
class QgsTextureAtlasGenerator
|
||||||
|
{
|
||||||
|
%Docstring(signature="appended")
|
||||||
|
Generates texture atlases by efficient packing of multiple input
|
||||||
|
rectangles/images.
|
||||||
|
|
||||||
|
:py:class:`QgsTextureAtlasGenerator` can be used to pack either images
|
||||||
|
or raw rectangles. The static :py:func:`~createFromRects` or
|
||||||
|
:py:func:`~createFromImages` methods should be called with the source
|
||||||
|
images or rectangles, which will return a :py:class:`QgsTextureAtlas`
|
||||||
|
containing the results.
|
||||||
|
|
||||||
|
.. versionadded:: 4.0
|
||||||
|
%End
|
||||||
|
|
||||||
|
%TypeHeaderCode
|
||||||
|
#include "qgstextureatlasgenerator.h"
|
||||||
|
%End
|
||||||
|
public:
|
||||||
|
static QgsTextureAtlas createFromRects( const QVector< QRect > &rectangles, int maxSide = 1000 );
|
||||||
|
%Docstring
|
||||||
|
Creates a texture atlas for a set of ``rectangles``.
|
||||||
|
|
||||||
|
This method should be used when the generator is used to pack rectangle
|
||||||
|
shapes only. No image will be associated with the rectangle.
|
||||||
|
|
||||||
|
The ``maxSide`` argument specifies the maximum permitted side size for
|
||||||
|
the atlas. The calculated solution can only be less than or equal to
|
||||||
|
this size - if it cannot fit, then algorithm will gracefully fail and
|
||||||
|
return an invalid :py:class:`QgsTextureAtlas`.
|
||||||
|
|
||||||
|
.. seealso:: :py:func:`createFromImages`
|
||||||
|
%End
|
||||||
|
|
||||||
|
static QgsTextureAtlas createFromImages( const QVector< QImage > &images, int maxSide = 1000 );
|
||||||
|
%Docstring
|
||||||
|
Creates a texture atlas for a set of ``images``.
|
||||||
|
|
||||||
|
The ``maxSide`` argument specifies the maximum permitted side size for
|
||||||
|
the atlas. The calculated solution can only be less than or equal to
|
||||||
|
this size - if it cannot fit, then algorithm will gracefully fail and
|
||||||
|
return an invalid :py:class:`QgsTextureAtlas`.
|
||||||
|
|
||||||
|
.. seealso:: :py:func:`createFromRects`
|
||||||
|
%End
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
/************************************************************************
|
||||||
|
* This file has been generated automatically from *
|
||||||
|
* *
|
||||||
|
* src/3d/qgstextureatlasgenerator.h *
|
||||||
|
* *
|
||||||
|
* Do not edit manually ! Edit header and run scripts/sipify.py again *
|
||||||
|
************************************************************************/
|
@ -246,6 +246,22 @@ QgsAbstractVectorLayer3DRenderer.setTilingSettings: src/3d/qgsabstractvectorlaye
|
|||||||
QgsAbstractVectorLayer3DRenderer.tilingSettings: src/3d/qgsabstractvectorlayer3drenderer.h#L89
|
QgsAbstractVectorLayer3DRenderer.tilingSettings: src/3d/qgsabstractvectorlayer3drenderer.h#L89
|
||||||
QgsAbstractVectorLayer3DRenderer.writeXmlBaseProperties: src/3d/qgsabstractvectorlayer3drenderer.h#L97
|
QgsAbstractVectorLayer3DRenderer.writeXmlBaseProperties: src/3d/qgsabstractvectorlayer3drenderer.h#L97
|
||||||
QgsAbstractVectorLayer3DRenderer: src/3d/qgsabstractvectorlayer3drenderer.h#L76
|
QgsAbstractVectorLayer3DRenderer: src/3d/qgsabstractvectorlayer3drenderer.h#L76
|
||||||
|
QgsAnnotationLayer3DRenderer.altitudeClamping: src/3d/qgsannotationlayer3drenderer.h#L100
|
||||||
|
QgsAnnotationLayer3DRenderer.clone: src/3d/qgsannotationlayer3drenderer.h#L88
|
||||||
|
QgsAnnotationLayer3DRenderer.layer: src/3d/qgsannotationlayer3drenderer.h#L85
|
||||||
|
QgsAnnotationLayer3DRenderer.readXml: src/3d/qgsannotationlayer3drenderer.h#L92
|
||||||
|
QgsAnnotationLayer3DRenderer.resolveReferences: src/3d/qgsannotationlayer3drenderer.h#L93
|
||||||
|
QgsAnnotationLayer3DRenderer.setAltitudeClamping: src/3d/qgsannotationlayer3drenderer.h#L107
|
||||||
|
QgsAnnotationLayer3DRenderer.setLayer: src/3d/qgsannotationlayer3drenderer.h#L78
|
||||||
|
QgsAnnotationLayer3DRenderer.setShowCalloutLines: src/3d/qgsannotationlayer3drenderer.h#L135
|
||||||
|
QgsAnnotationLayer3DRenderer.setZOffset: src/3d/qgsannotationlayer3drenderer.h#L121
|
||||||
|
QgsAnnotationLayer3DRenderer.showCalloutLines: src/3d/qgsannotationlayer3drenderer.h#L128
|
||||||
|
QgsAnnotationLayer3DRenderer.type: src/3d/qgsannotationlayer3drenderer.h#L87
|
||||||
|
QgsAnnotationLayer3DRenderer.writeXml: src/3d/qgsannotationlayer3drenderer.h#L91
|
||||||
|
QgsAnnotationLayer3DRenderer.zOffset: src/3d/qgsannotationlayer3drenderer.h#L114
|
||||||
|
QgsAnnotationLayer3DRenderer: src/3d/qgsannotationlayer3drenderer.h#L59
|
||||||
|
QgsAnnotationLayer3DRendererMetadata.createRenderer: src/3d/qgsannotationlayer3drenderer.h#L50
|
||||||
|
QgsAnnotationLayer3DRendererMetadata: src/3d/qgsannotationlayer3drenderer.h#L44
|
||||||
QgsCameraController.cameraChanged: src/3d/qgscameracontroller.h#L360
|
QgsCameraController.cameraChanged: src/3d/qgscameracontroller.h#L360
|
||||||
QgsCameraController.cameraMovementSpeed: src/3d/qgscameracontroller.h#L89
|
QgsCameraController.cameraMovementSpeed: src/3d/qgscameracontroller.h#L89
|
||||||
QgsCameraController.cameraMovementSpeedChanged: src/3d/qgscameracontroller.h#L368
|
QgsCameraController.cameraMovementSpeedChanged: src/3d/qgscameracontroller.h#L368
|
||||||
@ -770,6 +786,17 @@ QgsSingleColorPointCloud3DSymbol.singleColor: src/3d/symbols/qgspointcloud3dsymb
|
|||||||
QgsSingleColorPointCloud3DSymbol.symbolType: src/3d/symbols/qgspointcloud3dsymbol.h#L202
|
QgsSingleColorPointCloud3DSymbol.symbolType: src/3d/symbols/qgspointcloud3dsymbol.h#L202
|
||||||
QgsSingleColorPointCloud3DSymbol.writeXml: src/3d/symbols/qgspointcloud3dsymbol.h#L205
|
QgsSingleColorPointCloud3DSymbol.writeXml: src/3d/symbols/qgspointcloud3dsymbol.h#L205
|
||||||
QgsSingleColorPointCloud3DSymbol: src/3d/symbols/qgspointcloud3dsymbol.h#L197
|
QgsSingleColorPointCloud3DSymbol: src/3d/symbols/qgspointcloud3dsymbol.h#L197
|
||||||
|
QgsTextureAtlas.__len__: src/3d/qgstextureatlasgenerator.h#L107
|
||||||
|
QgsTextureAtlas.atlasSize: src/3d/qgstextureatlasgenerator.h#L58
|
||||||
|
QgsTextureAtlas.count: src/3d/qgstextureatlasgenerator.h#L104
|
||||||
|
QgsTextureAtlas.isValid: src/3d/qgstextureatlasgenerator.h#L52
|
||||||
|
QgsTextureAtlas.rect: src/3d/qgstextureatlasgenerator.h#L71
|
||||||
|
QgsTextureAtlas.renderAtlasTexture: src/3d/qgstextureatlasgenerator.h#L91
|
||||||
|
QgsTextureAtlas.renderDebugTexture: src/3d/qgstextureatlasgenerator.h#L99
|
||||||
|
QgsTextureAtlas: src/3d/qgstextureatlasgenerator.h#L40
|
||||||
|
QgsTextureAtlasGenerator.createFromImages: src/3d/qgstextureatlasgenerator.h#L161
|
||||||
|
QgsTextureAtlasGenerator.createFromRects: src/3d/qgstextureatlasgenerator.h#L150
|
||||||
|
QgsTextureAtlasGenerator: src/3d/qgstextureatlasgenerator.h#L135
|
||||||
QgsTiledSceneLayer3DRenderer.clone: src/3d/qgstiledscenelayer3drenderer.h#L107
|
QgsTiledSceneLayer3DRenderer.clone: src/3d/qgstiledscenelayer3drenderer.h#L107
|
||||||
QgsTiledSceneLayer3DRenderer.layer: src/3d/qgstiledscenelayer3drenderer.h#L72
|
QgsTiledSceneLayer3DRenderer.layer: src/3d/qgstiledscenelayer3drenderer.h#L72
|
||||||
QgsTiledSceneLayer3DRenderer.maximumScreenError: src/3d/qgstiledscenelayer3drenderer.h#L81
|
QgsTiledSceneLayer3DRenderer.maximumScreenError: src/3d/qgstiledscenelayer3drenderer.h#L81
|
||||||
|
@ -262,7 +262,7 @@ if (WITH_3D)
|
|||||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/3d/project.py.in ${CMAKE_CURRENT_BINARY_DIR}/3d/project.py @ONLY)
|
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/3d/project.py.in ${CMAKE_CURRENT_BINARY_DIR}/3d/project.py @ONLY)
|
||||||
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/3d/pyproject.toml.temp" "${toml}")
|
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/3d/pyproject.toml.temp" "${toml}")
|
||||||
execute_process(COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_BINARY_DIR}/3d/pyproject.toml.temp ${CMAKE_CURRENT_BINARY_DIR}/3d/pyproject.toml)
|
execute_process(COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_BINARY_DIR}/3d/pyproject.toml.temp ${CMAKE_CURRENT_BINARY_DIR}/3d/pyproject.toml)
|
||||||
SET(SIP_CONCAT_PARTS 10) # 3D doesn't have enough .sip files to fill 15+ .cpp files
|
SET(SIP_CONCAT_PARTS 8) # 3D doesn't have enough .sip files to fill 15+ .cpp files
|
||||||
GENERATE_SIP_PYTHON_MODULE_CODE(qgis._3d_p 3d/3d.sip "${sip_files_3d}" cpp_files)
|
GENERATE_SIP_PYTHON_MODULE_CODE(qgis._3d_p 3d/3d.sip "${sip_files_3d}" cpp_files)
|
||||||
BUILD_SIP_PYTHON_MODULE(qgis._3d_p 3d/3d.sip ${cpp_files} "" qgis_core qgis_3d)
|
BUILD_SIP_PYTHON_MODULE(qgis._3d_p 3d/3d.sip ${cpp_files} "" qgis_core qgis_3d)
|
||||||
endif()
|
endif()
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
%Include auto_generated/qgs3dtypes.sip
|
%Include auto_generated/qgs3dtypes.sip
|
||||||
%Include auto_generated/qgs3dmapcanvas.sip
|
%Include auto_generated/qgs3dmapcanvas.sip
|
||||||
%Include auto_generated/qgsabstractvectorlayer3drenderer.sip
|
%Include auto_generated/qgsabstractvectorlayer3drenderer.sip
|
||||||
|
%Include auto_generated/qgsannotationlayer3drenderer.sip
|
||||||
%Include auto_generated/qgscameracontroller.sip
|
%Include auto_generated/qgscameracontroller.sip
|
||||||
%Include auto_generated/qgscamerapose.sip
|
%Include auto_generated/qgscamerapose.sip
|
||||||
%Include auto_generated/qgslayoutitem3dmap.sip
|
%Include auto_generated/qgslayoutitem3dmap.sip
|
||||||
@ -13,6 +14,7 @@
|
|||||||
%Include auto_generated/qgstiledscenelayer3drenderer.sip
|
%Include auto_generated/qgstiledscenelayer3drenderer.sip
|
||||||
%Include auto_generated/qgsvectorlayer3drenderer.sip
|
%Include auto_generated/qgsvectorlayer3drenderer.sip
|
||||||
%Include auto_generated/qgspointcloudlayer3drenderer.sip
|
%Include auto_generated/qgspointcloudlayer3drenderer.sip
|
||||||
|
%Include auto_generated/qgstextureatlasgenerator.sip
|
||||||
%Include auto_generated/lights/qgsdirectionallightsettings.sip
|
%Include auto_generated/lights/qgsdirectionallightsettings.sip
|
||||||
%Include auto_generated/lights/qgslightsource.sip
|
%Include auto_generated/lights/qgslightsource.sip
|
||||||
%Include auto_generated/lights/qgspointlightsettings.sip
|
%Include auto_generated/lights/qgspointlightsettings.sip
|
||||||
|
@ -0,0 +1,9 @@
|
|||||||
|
# The following has been generated automatically from src/3d/qgsannotationlayer3drenderer.h
|
||||||
|
try:
|
||||||
|
QgsAnnotationLayer3DRendererMetadata.__overridden_methods__ = ['createRenderer']
|
||||||
|
except (NameError, AttributeError):
|
||||||
|
pass
|
||||||
|
try:
|
||||||
|
QgsAnnotationLayer3DRenderer.__overridden_methods__ = ['type', 'clone', 'writeXml', 'readXml', 'resolveReferences']
|
||||||
|
except (NameError, AttributeError):
|
||||||
|
pass
|
@ -0,0 +1,6 @@
|
|||||||
|
# The following has been generated automatically from src/3d/qgstextureatlasgenerator.h
|
||||||
|
try:
|
||||||
|
QgsTextureAtlasGenerator.createFromRects = staticmethod(QgsTextureAtlasGenerator.createFromRects)
|
||||||
|
QgsTextureAtlasGenerator.createFromImages = staticmethod(QgsTextureAtlasGenerator.createFromImages)
|
||||||
|
except (NameError, AttributeError):
|
||||||
|
pass
|
@ -0,0 +1,154 @@
|
|||||||
|
/************************************************************************
|
||||||
|
* This file has been generated automatically from *
|
||||||
|
* *
|
||||||
|
* src/3d/qgsannotationlayer3drenderer.h *
|
||||||
|
* *
|
||||||
|
* Do not edit manually ! Edit header and run scripts/sipify.py again *
|
||||||
|
************************************************************************/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// this is needed for the "convert to subclass" code below to compile
|
||||||
|
%ModuleHeaderCode
|
||||||
|
#include "qgsannotationlayer3drenderer.h"
|
||||||
|
%End
|
||||||
|
|
||||||
|
class QgsAnnotationLayer3DRendererMetadata : Qgs3DRendererAbstractMetadata
|
||||||
|
{
|
||||||
|
%Docstring(signature="appended")
|
||||||
|
Metadata for annotation layer 3D renderer to allow creation of its
|
||||||
|
instances from XML.
|
||||||
|
|
||||||
|
.. warning::
|
||||||
|
|
||||||
|
This is not considered stable API, and may change in future QGIS releases. It is
|
||||||
|
exposed to the Python bindings as a tech preview only.
|
||||||
|
|
||||||
|
.. versionadded:: 4.0
|
||||||
|
%End
|
||||||
|
|
||||||
|
%TypeHeaderCode
|
||||||
|
#include "qgsannotationlayer3drenderer.h"
|
||||||
|
%End
|
||||||
|
public:
|
||||||
|
QgsAnnotationLayer3DRendererMetadata();
|
||||||
|
|
||||||
|
virtual QgsAbstract3DRenderer *createRenderer( QDomElement &elem, const QgsReadWriteContext &context ) /Factory/;
|
||||||
|
|
||||||
|
%Docstring
|
||||||
|
Creates an instance of a 3D renderer based on a DOM element with
|
||||||
|
renderer configuration
|
||||||
|
%End
|
||||||
|
};
|
||||||
|
|
||||||
|
class QgsAnnotationLayer3DRenderer : QgsAbstract3DRenderer
|
||||||
|
{
|
||||||
|
%Docstring(signature="appended")
|
||||||
|
3D renderers for annotation layers.
|
||||||
|
|
||||||
|
.. versionadded:: 4.0
|
||||||
|
%End
|
||||||
|
|
||||||
|
%TypeHeaderCode
|
||||||
|
#include "qgsannotationlayer3drenderer.h"
|
||||||
|
%End
|
||||||
|
%ConvertToSubClassCode
|
||||||
|
if ( dynamic_cast<QgsAnnotationLayer3DRenderer *>( sipCpp ) != nullptr )
|
||||||
|
sipType = sipType_QgsAnnotationLayer3DRenderer;
|
||||||
|
else
|
||||||
|
sipType = nullptr;
|
||||||
|
%End
|
||||||
|
public:
|
||||||
|
QgsAnnotationLayer3DRenderer();
|
||||||
|
|
||||||
|
void setLayer( QgsAnnotationLayer *layer );
|
||||||
|
%Docstring
|
||||||
|
Sets the annotation layer associated with the renderer.
|
||||||
|
|
||||||
|
.. seealso:: :py:func:`layer`
|
||||||
|
%End
|
||||||
|
|
||||||
|
QgsAnnotationLayer *layer() const;
|
||||||
|
%Docstring
|
||||||
|
Returns the annotation layer associated with the renderer.
|
||||||
|
|
||||||
|
.. seealso:: :py:func:`setLayer`
|
||||||
|
%End
|
||||||
|
|
||||||
|
virtual QString type() const;
|
||||||
|
|
||||||
|
virtual QgsAnnotationLayer3DRenderer *clone() const /Factory/;
|
||||||
|
|
||||||
|
|
||||||
|
virtual void writeXml( QDomElement &elem, const QgsReadWriteContext &context ) const;
|
||||||
|
|
||||||
|
virtual void readXml( const QDomElement &elem, const QgsReadWriteContext &context );
|
||||||
|
|
||||||
|
virtual void resolveReferences( const QgsProject &project );
|
||||||
|
|
||||||
|
|
||||||
|
Qgis::AltitudeClamping altitudeClamping() const;
|
||||||
|
%Docstring
|
||||||
|
Returns the altitude clamping method, which determines the vertical
|
||||||
|
position of annotations.
|
||||||
|
|
||||||
|
.. seealso:: :py:func:`setAltitudeClamping`
|
||||||
|
%End
|
||||||
|
|
||||||
|
void setAltitudeClamping( Qgis::AltitudeClamping clamping );
|
||||||
|
%Docstring
|
||||||
|
Sets the altitude ``clamping`` method, which determines the vertical
|
||||||
|
position of annotations.
|
||||||
|
|
||||||
|
.. seealso:: :py:func:`altitudeClamping`
|
||||||
|
%End
|
||||||
|
|
||||||
|
double zOffset() const;
|
||||||
|
%Docstring
|
||||||
|
Returns the z offset, which is a fixed offset amount which should be
|
||||||
|
added to z values for the annotations.
|
||||||
|
|
||||||
|
.. seealso:: :py:func:`setZOffset`
|
||||||
|
%End
|
||||||
|
|
||||||
|
void setZOffset( double offset );
|
||||||
|
%Docstring
|
||||||
|
Sets the z ``offset``, which is a fixed offset amount which will be
|
||||||
|
added to z values for the annotations.
|
||||||
|
|
||||||
|
.. seealso:: :py:func:`zOffset`
|
||||||
|
%End
|
||||||
|
|
||||||
|
bool showCalloutLines() const;
|
||||||
|
%Docstring
|
||||||
|
Returns ``True`` if callout lines are shown, vertically joining the
|
||||||
|
annotations to the terrain.
|
||||||
|
|
||||||
|
.. seealso:: :py:func:`setShowCalloutLines`
|
||||||
|
%End
|
||||||
|
|
||||||
|
void setShowCalloutLines( bool show );
|
||||||
|
%Docstring
|
||||||
|
Sets whether callout lines are shown, vertically joining the annotations
|
||||||
|
to the terrain.
|
||||||
|
|
||||||
|
.. seealso:: :py:func:`showCalloutLines`
|
||||||
|
%End
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private:
|
||||||
|
QgsAnnotationLayer3DRenderer( const QgsAnnotationLayer3DRenderer & );
|
||||||
|
};
|
||||||
|
|
||||||
|
/************************************************************************
|
||||||
|
* This file has been generated automatically from *
|
||||||
|
* *
|
||||||
|
* src/3d/qgsannotationlayer3drenderer.h *
|
||||||
|
* *
|
||||||
|
* Do not edit manually ! Edit header and run scripts/sipify.py again *
|
||||||
|
************************************************************************/
|
154
python/PyQt6/3d/auto_generated/qgstextureatlasgenerator.sip.in
Normal file
154
python/PyQt6/3d/auto_generated/qgstextureatlasgenerator.sip.in
Normal file
@ -0,0 +1,154 @@
|
|||||||
|
/************************************************************************
|
||||||
|
* This file has been generated automatically from *
|
||||||
|
* *
|
||||||
|
* src/3d/qgstextureatlasgenerator.h *
|
||||||
|
* *
|
||||||
|
* Do not edit manually ! Edit header and run scripts/sipify.py again *
|
||||||
|
************************************************************************/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class QgsTextureAtlas
|
||||||
|
{
|
||||||
|
%Docstring(signature="appended")
|
||||||
|
Encapsulates a texture atlas.
|
||||||
|
|
||||||
|
:py:class:`QgsTextureAtlas` contains the packed regions for aggregated
|
||||||
|
texture atlases, and optionally the packed texture map.
|
||||||
|
|
||||||
|
See :py:class:`QgsTextureAtlasGenerator` for a class which automatically
|
||||||
|
creates texture atlases.
|
||||||
|
|
||||||
|
.. versionadded:: 4.0
|
||||||
|
%End
|
||||||
|
|
||||||
|
%TypeHeaderCode
|
||||||
|
#include "qgstextureatlasgenerator.h"
|
||||||
|
%End
|
||||||
|
public:
|
||||||
|
QgsTextureAtlas();
|
||||||
|
~QgsTextureAtlas();
|
||||||
|
|
||||||
|
QgsTextureAtlas( const QgsTextureAtlas &other );
|
||||||
|
|
||||||
|
bool isValid() const;
|
||||||
|
%Docstring
|
||||||
|
Returns ``True`` if the atlas is valid.
|
||||||
|
%End
|
||||||
|
|
||||||
|
QSize atlasSize() const;
|
||||||
|
%Docstring
|
||||||
|
Returns the total size required for the atlas, i.e. the total size for
|
||||||
|
the packed images and rectangles.
|
||||||
|
%End
|
||||||
|
|
||||||
|
QRect rect( int index ) const;
|
||||||
|
%Docstring
|
||||||
|
Returns the packed rectangle for the texture with the specified
|
||||||
|
``index``.
|
||||||
|
|
||||||
|
:raises IndexError: if no texture with the specified index exists.
|
||||||
|
%End
|
||||||
|
%MethodCode
|
||||||
|
const int count = sipCpp->count();
|
||||||
|
if ( a0 < 0 || a0 >= count )
|
||||||
|
{
|
||||||
|
PyErr_SetString( PyExc_IndexError, QByteArray::number( a0 ) );
|
||||||
|
sipIsErr = 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return sipConvertFromNewType( new QRect( sipCpp->rect( a0 ) ), sipType_QRect, Py_None );
|
||||||
|
}
|
||||||
|
%End
|
||||||
|
|
||||||
|
QImage renderAtlasTexture() const;
|
||||||
|
%Docstring
|
||||||
|
Renders the combined texture atlas, containing all source images.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
This may be a null image if the atlas was created with rectangles alone.
|
||||||
|
%End
|
||||||
|
|
||||||
|
QImage renderDebugTexture() const;
|
||||||
|
%Docstring
|
||||||
|
Renders a debug texture.
|
||||||
|
|
||||||
|
The debug texture renders all packed rectangles with a unique color, and
|
||||||
|
can be used to visualize the solution.
|
||||||
|
%End
|
||||||
|
|
||||||
|
int count() const;
|
||||||
|
%Docstring
|
||||||
|
Returns the number of textures in the atlas.
|
||||||
|
%End
|
||||||
|
|
||||||
|
Py_ssize_t __len__() const;
|
||||||
|
%Docstring
|
||||||
|
Returns the number of textures in the atlas.
|
||||||
|
%End
|
||||||
|
%MethodCode
|
||||||
|
sipRes
|
||||||
|
= sipCpp->count();
|
||||||
|
%End
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
class QgsTextureAtlasGenerator
|
||||||
|
{
|
||||||
|
%Docstring(signature="appended")
|
||||||
|
Generates texture atlases by efficient packing of multiple input
|
||||||
|
rectangles/images.
|
||||||
|
|
||||||
|
:py:class:`QgsTextureAtlasGenerator` can be used to pack either images
|
||||||
|
or raw rectangles. The static :py:func:`~createFromRects` or
|
||||||
|
:py:func:`~createFromImages` methods should be called with the source
|
||||||
|
images or rectangles, which will return a :py:class:`QgsTextureAtlas`
|
||||||
|
containing the results.
|
||||||
|
|
||||||
|
.. versionadded:: 4.0
|
||||||
|
%End
|
||||||
|
|
||||||
|
%TypeHeaderCode
|
||||||
|
#include "qgstextureatlasgenerator.h"
|
||||||
|
%End
|
||||||
|
public:
|
||||||
|
static QgsTextureAtlas createFromRects( const QVector< QRect > &rectangles, int maxSide = 1000 );
|
||||||
|
%Docstring
|
||||||
|
Creates a texture atlas for a set of ``rectangles``.
|
||||||
|
|
||||||
|
This method should be used when the generator is used to pack rectangle
|
||||||
|
shapes only. No image will be associated with the rectangle.
|
||||||
|
|
||||||
|
The ``maxSide`` argument specifies the maximum permitted side size for
|
||||||
|
the atlas. The calculated solution can only be less than or equal to
|
||||||
|
this size - if it cannot fit, then algorithm will gracefully fail and
|
||||||
|
return an invalid :py:class:`QgsTextureAtlas`.
|
||||||
|
|
||||||
|
.. seealso:: :py:func:`createFromImages`
|
||||||
|
%End
|
||||||
|
|
||||||
|
static QgsTextureAtlas createFromImages( const QVector< QImage > &images, int maxSide = 1000 );
|
||||||
|
%Docstring
|
||||||
|
Creates a texture atlas for a set of ``images``.
|
||||||
|
|
||||||
|
The ``maxSide`` argument specifies the maximum permitted side size for
|
||||||
|
the atlas. The calculated solution can only be less than or equal to
|
||||||
|
this size - if it cannot fit, then algorithm will gracefully fail and
|
||||||
|
return an invalid :py:class:`QgsTextureAtlas`.
|
||||||
|
|
||||||
|
.. seealso:: :py:func:`createFromRects`
|
||||||
|
%End
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
/************************************************************************
|
||||||
|
* This file has been generated automatically from *
|
||||||
|
* *
|
||||||
|
* src/3d/qgstextureatlasgenerator.h *
|
||||||
|
* *
|
||||||
|
* Do not edit manually ! Edit header and run scripts/sipify.py again *
|
||||||
|
************************************************************************/
|
@ -246,6 +246,22 @@ QgsAbstractVectorLayer3DRenderer.setTilingSettings: src/3d/qgsabstractvectorlaye
|
|||||||
QgsAbstractVectorLayer3DRenderer.tilingSettings: src/3d/qgsabstractvectorlayer3drenderer.h#L89
|
QgsAbstractVectorLayer3DRenderer.tilingSettings: src/3d/qgsabstractvectorlayer3drenderer.h#L89
|
||||||
QgsAbstractVectorLayer3DRenderer.writeXmlBaseProperties: src/3d/qgsabstractvectorlayer3drenderer.h#L97
|
QgsAbstractVectorLayer3DRenderer.writeXmlBaseProperties: src/3d/qgsabstractvectorlayer3drenderer.h#L97
|
||||||
QgsAbstractVectorLayer3DRenderer: src/3d/qgsabstractvectorlayer3drenderer.h#L76
|
QgsAbstractVectorLayer3DRenderer: src/3d/qgsabstractvectorlayer3drenderer.h#L76
|
||||||
|
QgsAnnotationLayer3DRenderer.altitudeClamping: src/3d/qgsannotationlayer3drenderer.h#L100
|
||||||
|
QgsAnnotationLayer3DRenderer.clone: src/3d/qgsannotationlayer3drenderer.h#L88
|
||||||
|
QgsAnnotationLayer3DRenderer.layer: src/3d/qgsannotationlayer3drenderer.h#L85
|
||||||
|
QgsAnnotationLayer3DRenderer.readXml: src/3d/qgsannotationlayer3drenderer.h#L92
|
||||||
|
QgsAnnotationLayer3DRenderer.resolveReferences: src/3d/qgsannotationlayer3drenderer.h#L93
|
||||||
|
QgsAnnotationLayer3DRenderer.setAltitudeClamping: src/3d/qgsannotationlayer3drenderer.h#L107
|
||||||
|
QgsAnnotationLayer3DRenderer.setLayer: src/3d/qgsannotationlayer3drenderer.h#L78
|
||||||
|
QgsAnnotationLayer3DRenderer.setShowCalloutLines: src/3d/qgsannotationlayer3drenderer.h#L135
|
||||||
|
QgsAnnotationLayer3DRenderer.setZOffset: src/3d/qgsannotationlayer3drenderer.h#L121
|
||||||
|
QgsAnnotationLayer3DRenderer.showCalloutLines: src/3d/qgsannotationlayer3drenderer.h#L128
|
||||||
|
QgsAnnotationLayer3DRenderer.type: src/3d/qgsannotationlayer3drenderer.h#L87
|
||||||
|
QgsAnnotationLayer3DRenderer.writeXml: src/3d/qgsannotationlayer3drenderer.h#L91
|
||||||
|
QgsAnnotationLayer3DRenderer.zOffset: src/3d/qgsannotationlayer3drenderer.h#L114
|
||||||
|
QgsAnnotationLayer3DRenderer: src/3d/qgsannotationlayer3drenderer.h#L59
|
||||||
|
QgsAnnotationLayer3DRendererMetadata.createRenderer: src/3d/qgsannotationlayer3drenderer.h#L50
|
||||||
|
QgsAnnotationLayer3DRendererMetadata: src/3d/qgsannotationlayer3drenderer.h#L44
|
||||||
QgsCameraController.cameraChanged: src/3d/qgscameracontroller.h#L360
|
QgsCameraController.cameraChanged: src/3d/qgscameracontroller.h#L360
|
||||||
QgsCameraController.cameraMovementSpeed: src/3d/qgscameracontroller.h#L89
|
QgsCameraController.cameraMovementSpeed: src/3d/qgscameracontroller.h#L89
|
||||||
QgsCameraController.cameraMovementSpeedChanged: src/3d/qgscameracontroller.h#L368
|
QgsCameraController.cameraMovementSpeedChanged: src/3d/qgscameracontroller.h#L368
|
||||||
@ -770,6 +786,17 @@ QgsSingleColorPointCloud3DSymbol.singleColor: src/3d/symbols/qgspointcloud3dsymb
|
|||||||
QgsSingleColorPointCloud3DSymbol.symbolType: src/3d/symbols/qgspointcloud3dsymbol.h#L202
|
QgsSingleColorPointCloud3DSymbol.symbolType: src/3d/symbols/qgspointcloud3dsymbol.h#L202
|
||||||
QgsSingleColorPointCloud3DSymbol.writeXml: src/3d/symbols/qgspointcloud3dsymbol.h#L205
|
QgsSingleColorPointCloud3DSymbol.writeXml: src/3d/symbols/qgspointcloud3dsymbol.h#L205
|
||||||
QgsSingleColorPointCloud3DSymbol: src/3d/symbols/qgspointcloud3dsymbol.h#L197
|
QgsSingleColorPointCloud3DSymbol: src/3d/symbols/qgspointcloud3dsymbol.h#L197
|
||||||
|
QgsTextureAtlas.__len__: src/3d/qgstextureatlasgenerator.h#L107
|
||||||
|
QgsTextureAtlas.atlasSize: src/3d/qgstextureatlasgenerator.h#L58
|
||||||
|
QgsTextureAtlas.count: src/3d/qgstextureatlasgenerator.h#L104
|
||||||
|
QgsTextureAtlas.isValid: src/3d/qgstextureatlasgenerator.h#L52
|
||||||
|
QgsTextureAtlas.rect: src/3d/qgstextureatlasgenerator.h#L71
|
||||||
|
QgsTextureAtlas.renderAtlasTexture: src/3d/qgstextureatlasgenerator.h#L91
|
||||||
|
QgsTextureAtlas.renderDebugTexture: src/3d/qgstextureatlasgenerator.h#L99
|
||||||
|
QgsTextureAtlas: src/3d/qgstextureatlasgenerator.h#L40
|
||||||
|
QgsTextureAtlasGenerator.createFromImages: src/3d/qgstextureatlasgenerator.h#L161
|
||||||
|
QgsTextureAtlasGenerator.createFromRects: src/3d/qgstextureatlasgenerator.h#L150
|
||||||
|
QgsTextureAtlasGenerator: src/3d/qgstextureatlasgenerator.h#L135
|
||||||
QgsTiledSceneLayer3DRenderer.clone: src/3d/qgstiledscenelayer3drenderer.h#L107
|
QgsTiledSceneLayer3DRenderer.clone: src/3d/qgstiledscenelayer3drenderer.h#L107
|
||||||
QgsTiledSceneLayer3DRenderer.layer: src/3d/qgstiledscenelayer3drenderer.h#L72
|
QgsTiledSceneLayer3DRenderer.layer: src/3d/qgstiledscenelayer3drenderer.h#L72
|
||||||
QgsTiledSceneLayer3DRenderer.maximumScreenError: src/3d/qgstiledscenelayer3drenderer.h#L81
|
QgsTiledSceneLayer3DRenderer.maximumScreenError: src/3d/qgstiledscenelayer3drenderer.h#L81
|
||||||
|
@ -11742,6 +11742,25 @@ Qgis.RasterProcessingParameterCapability.baseClass = Qgis
|
|||||||
Qgis.RasterProcessingParameterCapabilities = lambda flags=0: Qgis.RasterProcessingParameterCapability(flags)
|
Qgis.RasterProcessingParameterCapabilities = lambda flags=0: Qgis.RasterProcessingParameterCapability(flags)
|
||||||
Qgis.RasterProcessingParameterCapabilities.baseClass = Qgis
|
Qgis.RasterProcessingParameterCapabilities.baseClass = Qgis
|
||||||
RasterProcessingParameterCapabilities = Qgis # dirty hack since SIP seems to introduce the flags in module
|
RasterProcessingParameterCapabilities = Qgis # dirty hack since SIP seems to introduce the flags in module
|
||||||
|
# monkey patching scoped based enum
|
||||||
|
Qgis.DevToolsNodeRole.Status.__doc__ = "Request status role"
|
||||||
|
Qgis.DevToolsNodeRole.Id.__doc__ = "Request ID role"
|
||||||
|
Qgis.DevToolsNodeRole.ElapsedTime.__doc__ = "Elapsed time"
|
||||||
|
Qgis.DevToolsNodeRole.MaximumTime.__doc__ = "Maximum encountered elapsed time"
|
||||||
|
Qgis.DevToolsNodeRole.Sort.__doc__ = "Sort order role"
|
||||||
|
Qgis.DevToolsNodeRole.__doc__ = """Dev tools node custom data roles.
|
||||||
|
|
||||||
|
.. versionadded:: 4.0
|
||||||
|
|
||||||
|
* ``Status``: Request status role
|
||||||
|
* ``Id``: Request ID role
|
||||||
|
* ``ElapsedTime``: Elapsed time
|
||||||
|
* ``MaximumTime``: Maximum encountered elapsed time
|
||||||
|
* ``Sort``: Sort order role
|
||||||
|
|
||||||
|
"""
|
||||||
|
# --
|
||||||
|
Qgis.DevToolsNodeRole.baseClass = Qgis
|
||||||
try:
|
try:
|
||||||
Qgis.__attribute_docs__ = {'QGIS_DEV_VERSION': 'The development version', 'DEFAULT_SEARCH_RADIUS_MM': 'Identify search radius in mm', 'DEFAULT_MAPTOPIXEL_THRESHOLD': 'Default threshold between map coordinates and device coordinates for map2pixel simplification', 'DEFAULT_HIGHLIGHT_COLOR': 'Default highlight color. The transparency is expected to only be applied to polygon\nfill. Lines and outlines are rendered opaque.', 'DEFAULT_HIGHLIGHT_BUFFER_MM': 'Default highlight buffer in mm.', 'DEFAULT_HIGHLIGHT_MIN_WIDTH_MM': 'Default highlight line/stroke minimum width in mm.', 'SCALE_PRECISION': 'Fudge factor used to compare two scales. The code is often going from scale to scale\ndenominator. So it looses precision and, when a limit is inclusive, can lead to errors.\nTo avoid that, use this factor instead of using <= or >=.\n\n.. deprecated:: 3.40\n\n No longer used by QGIS and will be removed in QGIS 4.0.', 'DEFAULT_Z_COORDINATE': 'Default Z coordinate value.\nThis value have to be assigned to the Z coordinate for the vertex.', 'DEFAULT_M_COORDINATE': 'Default M coordinate value.\nThis value have to be assigned to the M coordinate for the vertex.\n\n.. versionadded:: 3.20', 'UI_SCALE_FACTOR': 'UI scaling factor. This should be applied to all widget sizes obtained from font metrics,\nto account for differences in the default font sizes across different platforms.', 'DEFAULT_SNAP_TOLERANCE': 'Default snapping distance tolerance.', 'DEFAULT_SNAP_UNITS': 'Default snapping distance units.', 'USER_CRS_START_ID': 'Minimum ID number for a user-defined projection.', 'DEFAULT_POINT_SIZE': 'The default size (in millimeters) for point marker symbols', 'DEFAULT_LINE_WIDTH': 'The default width (in millimeters) for line symbols', 'DEFAULT_SEGMENT_EPSILON': 'Default snapping tolerance for segments'}
|
Qgis.__attribute_docs__ = {'QGIS_DEV_VERSION': 'The development version', 'DEFAULT_SEARCH_RADIUS_MM': 'Identify search radius in mm', 'DEFAULT_MAPTOPIXEL_THRESHOLD': 'Default threshold between map coordinates and device coordinates for map2pixel simplification', 'DEFAULT_HIGHLIGHT_COLOR': 'Default highlight color. The transparency is expected to only be applied to polygon\nfill. Lines and outlines are rendered opaque.', 'DEFAULT_HIGHLIGHT_BUFFER_MM': 'Default highlight buffer in mm.', 'DEFAULT_HIGHLIGHT_MIN_WIDTH_MM': 'Default highlight line/stroke minimum width in mm.', 'SCALE_PRECISION': 'Fudge factor used to compare two scales. The code is often going from scale to scale\ndenominator. So it looses precision and, when a limit is inclusive, can lead to errors.\nTo avoid that, use this factor instead of using <= or >=.\n\n.. deprecated:: 3.40\n\n No longer used by QGIS and will be removed in QGIS 4.0.', 'DEFAULT_Z_COORDINATE': 'Default Z coordinate value.\nThis value have to be assigned to the Z coordinate for the vertex.', 'DEFAULT_M_COORDINATE': 'Default M coordinate value.\nThis value have to be assigned to the M coordinate for the vertex.\n\n.. versionadded:: 3.20', 'UI_SCALE_FACTOR': 'UI scaling factor. This should be applied to all widget sizes obtained from font metrics,\nto account for differences in the default font sizes across different platforms.', 'DEFAULT_SNAP_TOLERANCE': 'Default snapping distance tolerance.', 'DEFAULT_SNAP_UNITS': 'Default snapping distance units.', 'USER_CRS_START_ID': 'Minimum ID number for a user-defined projection.', 'DEFAULT_POINT_SIZE': 'The default size (in millimeters) for point marker symbols', 'DEFAULT_LINE_WIDTH': 'The default width (in millimeters) for line symbols', 'DEFAULT_SEGMENT_EPSILON': 'Default snapping tolerance for segments'}
|
||||||
Qgis.__annotations__ = {'QGIS_DEV_VERSION': str, 'DEFAULT_SEARCH_RADIUS_MM': float, 'DEFAULT_MAPTOPIXEL_THRESHOLD': float, 'DEFAULT_HIGHLIGHT_COLOR': 'QColor', 'DEFAULT_HIGHLIGHT_BUFFER_MM': float, 'DEFAULT_HIGHLIGHT_MIN_WIDTH_MM': float, 'SCALE_PRECISION': float, 'DEFAULT_Z_COORDINATE': float, 'DEFAULT_M_COORDINATE': float, 'UI_SCALE_FACTOR': float, 'DEFAULT_SNAP_TOLERANCE': float, 'DEFAULT_SNAP_UNITS': 'Qgis.MapToolUnit', 'USER_CRS_START_ID': int, 'DEFAULT_POINT_SIZE': float, 'DEFAULT_LINE_WIDTH': float, 'DEFAULT_SEGMENT_EPSILON': float}
|
Qgis.__annotations__ = {'QGIS_DEV_VERSION': str, 'DEFAULT_SEARCH_RADIUS_MM': float, 'DEFAULT_MAPTOPIXEL_THRESHOLD': float, 'DEFAULT_HIGHLIGHT_COLOR': 'QColor', 'DEFAULT_HIGHLIGHT_BUFFER_MM': float, 'DEFAULT_HIGHLIGHT_MIN_WIDTH_MM': float, 'SCALE_PRECISION': float, 'DEFAULT_Z_COORDINATE': float, 'DEFAULT_M_COORDINATE': float, 'UI_SCALE_FACTOR': float, 'DEFAULT_SNAP_TOLERANCE': float, 'DEFAULT_SNAP_UNITS': 'Qgis.MapToolUnit', 'USER_CRS_START_ID': int, 'DEFAULT_POINT_SIZE': float, 'DEFAULT_LINE_WIDTH': float, 'DEFAULT_SEGMENT_EPSILON': float}
|
||||||
|
@ -3446,6 +3446,15 @@ The development version
|
|||||||
typedef QFlags<Qgis::RasterProcessingParameterCapability> RasterProcessingParameterCapabilities;
|
typedef QFlags<Qgis::RasterProcessingParameterCapability> RasterProcessingParameterCapabilities;
|
||||||
|
|
||||||
|
|
||||||
|
enum class DevToolsNodeRole
|
||||||
|
{
|
||||||
|
Status,
|
||||||
|
Id,
|
||||||
|
ElapsedTime,
|
||||||
|
MaximumTime,
|
||||||
|
Sort,
|
||||||
|
};
|
||||||
|
|
||||||
static const double DEFAULT_SEARCH_RADIUS_MM;
|
static const double DEFAULT_SEARCH_RADIUS_MM;
|
||||||
|
|
||||||
static const float DEFAULT_MAPTOPIXEL_THRESHOLD;
|
static const float DEFAULT_MAPTOPIXEL_THRESHOLD;
|
||||||
|
@ -73,18 +73,15 @@ Returns the format name for cloud optimized formats
|
|||||||
.. versionadded:: 3.42
|
.. versionadded:: 3.42
|
||||||
%End
|
%End
|
||||||
|
|
||||||
QgsMimeDataUtils::Uri uri() const;
|
QgsMimeDataUtils::Uri uri( const QString &authcfg = QString() ) const;
|
||||||
%Docstring
|
%Docstring
|
||||||
Returns a uri for the asset if it is a cloud optimized file like COG or
|
Returns a uri for the asset if it is a cloud optimized file like COG or
|
||||||
COPC, empty auth configuration
|
COPC.
|
||||||
|
|
||||||
.. versionadded:: 3.42
|
:param authcfg: Optional authentication configuration ID.
|
||||||
%End
|
|
||||||
|
|
||||||
QgsMimeDataUtils::Uri uri( const QString &authcfg ) const;
|
If the optional @authcfg parameter is set the authentication
|
||||||
%Docstring
|
configuration ID will be encoded in the returned URI.
|
||||||
Returns a uri for the asset if it is a cloud optimized file like COG or
|
|
||||||
COPC
|
|
||||||
|
|
||||||
.. versionadded:: 4.0
|
.. versionadded:: 4.0
|
||||||
%End
|
%End
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
Qgis.defaultProjectScales: src/core/qgis.h#L6171
|
Qgis.defaultProjectScales: src/core/qgis.h#L6185
|
||||||
Qgis.devVersion: src/core/qgis.h#L90
|
Qgis.devVersion: src/core/qgis.h#L90
|
||||||
Qgis.geoNone: src/core/qgis.h#L6223
|
Qgis.geoNone: src/core/qgis.h#L6237
|
||||||
Qgis.geoProj4: src/core/qgis.h#L6253
|
Qgis.geoProj4: src/core/qgis.h#L6267
|
||||||
Qgis.geoWkt: src/core/qgis.h#L6244
|
Qgis.geoWkt: src/core/qgis.h#L6258
|
||||||
Qgis.geographicCrsAuthId: src/core/qgis.h#L6233
|
Qgis.geographicCrsAuthId: src/core/qgis.h#L6247
|
||||||
Qgis.geosVersion: src/core/qgis.h#L6206
|
Qgis.geosVersion: src/core/qgis.h#L6220
|
||||||
Qgis.geosVersionInt: src/core/qgis.h#L6178
|
Qgis.geosVersionInt: src/core/qgis.h#L6192
|
||||||
Qgis.geosVersionMajor: src/core/qgis.h#L6185
|
Qgis.geosVersionMajor: src/core/qgis.h#L6199
|
||||||
Qgis.geosVersionMinor: src/core/qgis.h#L6192
|
Qgis.geosVersionMinor: src/core/qgis.h#L6206
|
||||||
Qgis.geosVersionPatch: src/core/qgis.h#L6199
|
Qgis.geosVersionPatch: src/core/qgis.h#L6213
|
||||||
Qgis.hasQtWebkit: src/core/qgis.h#L6213
|
Qgis.hasQtWebkit: src/core/qgis.h#L6227
|
||||||
Qgis.releaseName: src/core/qgis.h#L80
|
Qgis.releaseName: src/core/qgis.h#L80
|
||||||
Qgis.version: src/core/qgis.h#L66
|
Qgis.version: src/core/qgis.h#L66
|
||||||
Qgis.versionInt: src/core/qgis.h#L73
|
Qgis.versionInt: src/core/qgis.h#L73
|
||||||
|
18
python/PyQt6/gui/auto_additions/qgsshortcutsmanager.py
Normal file
18
python/PyQt6/gui/auto_additions/qgsshortcutsmanager.py
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
# The following has been generated automatically from src/gui/qgsshortcutsmanager.h
|
||||||
|
# monkey patching scoped based enum
|
||||||
|
QgsShortcutsManager.CommonAction.CodeToggleComment.__doc__ = "Toggle code comments"
|
||||||
|
QgsShortcutsManager.CommonAction.CodeReformat.__doc__ = "Reformat code"
|
||||||
|
QgsShortcutsManager.CommonAction.CodeRunScript.__doc__ = "Run script"
|
||||||
|
QgsShortcutsManager.CommonAction.CodeRunSelection.__doc__ = "Run selection from script"
|
||||||
|
QgsShortcutsManager.CommonAction.__doc__ = """Contains common actions which are used across a variety of classes.
|
||||||
|
|
||||||
|
.. versionadded:: 4.0
|
||||||
|
|
||||||
|
* ``CodeToggleComment``: Toggle code comments
|
||||||
|
* ``CodeReformat``: Reformat code
|
||||||
|
* ``CodeRunScript``: Run script
|
||||||
|
* ``CodeRunSelection``: Run selection from script
|
||||||
|
|
||||||
|
"""
|
||||||
|
# --
|
||||||
|
QgsShortcutsManager.CommonAction.baseClass = QgsShortcutsManager
|
@ -23,6 +23,14 @@ rather accessed through :py:func:`QgsGui.shortcutsManager()`.
|
|||||||
#include "qgsshortcutsmanager.h"
|
#include "qgsshortcutsmanager.h"
|
||||||
%End
|
%End
|
||||||
public:
|
public:
|
||||||
|
enum class CommonAction
|
||||||
|
{
|
||||||
|
CodeToggleComment,
|
||||||
|
CodeReformat,
|
||||||
|
CodeRunScript,
|
||||||
|
CodeRunSelection,
|
||||||
|
};
|
||||||
|
|
||||||
QgsShortcutsManager( QObject *parent /TransferThis/ = 0, const QString &settingsRoot = "/shortcuts/" );
|
QgsShortcutsManager( QObject *parent /TransferThis/ = 0, const QString &settingsRoot = "/shortcuts/" );
|
||||||
%Docstring
|
%Docstring
|
||||||
Constructor for QgsShortcutsManager.
|
Constructor for QgsShortcutsManager.
|
||||||
@ -36,6 +44,8 @@ Constructor for QgsShortcutsManager.
|
|||||||
QGIS actions.
|
QGIS actions.
|
||||||
%End
|
%End
|
||||||
|
|
||||||
|
~QgsShortcutsManager();
|
||||||
|
|
||||||
void registerAllChildren( QObject *object, bool recursive = false, const QString §ion = QString() );
|
void registerAllChildren( QObject *object, bool recursive = false, const QString §ion = QString() );
|
||||||
%Docstring
|
%Docstring
|
||||||
Automatically registers all QActions and QShortcuts which are children
|
Automatically registers all QActions and QShortcuts which are children
|
||||||
@ -105,6 +115,16 @@ in GUI.
|
|||||||
.. seealso:: :py:func:`unregisterAction`
|
.. seealso:: :py:func:`unregisterAction`
|
||||||
|
|
||||||
.. seealso:: :py:func:`registerAllChildActions`
|
.. seealso:: :py:func:`registerAllChildActions`
|
||||||
|
%End
|
||||||
|
|
||||||
|
void initializeCommonAction( QAction *action, CommonAction commonAction );
|
||||||
|
%Docstring
|
||||||
|
Initializes an ``action`` as a common action.
|
||||||
|
|
||||||
|
This automatically configures the ``action`` to use the properties for
|
||||||
|
the common action, such as setting the action's tooltip and shortcut.
|
||||||
|
|
||||||
|
.. versionadded:: 4.0
|
||||||
%End
|
%End
|
||||||
|
|
||||||
bool registerShortcut( QShortcut *shortcut, const QString &defaultSequence = QString(), const QString §ion = QString() );
|
bool registerShortcut( QShortcut *shortcut, const QString &defaultSequence = QString(), const QString §ion = QString() );
|
||||||
@ -290,6 +310,14 @@ if no shortcut is associated.
|
|||||||
.. seealso:: :py:func:`objectForSequence`
|
.. seealso:: :py:func:`objectForSequence`
|
||||||
|
|
||||||
.. seealso:: :py:func:`actionForSequence`
|
.. seealso:: :py:func:`actionForSequence`
|
||||||
|
%End
|
||||||
|
|
||||||
|
QKeySequence sequenceForCommonAction( CommonAction action ) const;
|
||||||
|
%Docstring
|
||||||
|
Returns the key sequence which is associated with a common ``action``,
|
||||||
|
or an empty sequence if no shortcut is assigned to that action.
|
||||||
|
|
||||||
|
.. versionadded:: 4.0
|
||||||
%End
|
%End
|
||||||
|
|
||||||
QAction *actionByName( const QString &name ) const;
|
QAction *actionByName( const QString &name ) const;
|
||||||
|
@ -6977,28 +6977,30 @@ QgsShapeburstFillSymbolLayerWidget.setColor: src/gui/symbology/qgssymbollayerwid
|
|||||||
QgsShapeburstFillSymbolLayerWidget.setSymbolLayer: src/gui/symbology/qgssymbollayerwidget.h#L426
|
QgsShapeburstFillSymbolLayerWidget.setSymbolLayer: src/gui/symbology/qgssymbollayerwidget.h#L426
|
||||||
QgsShapeburstFillSymbolLayerWidget.symbolLayer: src/gui/symbology/qgssymbollayerwidget.h#L427
|
QgsShapeburstFillSymbolLayerWidget.symbolLayer: src/gui/symbology/qgssymbollayerwidget.h#L427
|
||||||
QgsShapeburstFillSymbolLayerWidget: src/gui/symbology/qgssymbollayerwidget.h#L407
|
QgsShapeburstFillSymbolLayerWidget: src/gui/symbology/qgssymbollayerwidget.h#L407
|
||||||
QgsShortcutsManager.actionByName: src/gui/qgsshortcutsmanager.h#L236
|
QgsShortcutsManager.actionByName: src/gui/qgsshortcutsmanager.h#L267
|
||||||
QgsShortcutsManager.actionForSequence: src/gui/qgsshortcutsmanager.h#L221
|
QgsShortcutsManager.actionForSequence: src/gui/qgsshortcutsmanager.h#L246
|
||||||
QgsShortcutsManager.defaultKeySequence: src/gui/qgsshortcutsmanager.h#L163
|
QgsShortcutsManager.defaultKeySequence: src/gui/qgsshortcutsmanager.h#L188
|
||||||
QgsShortcutsManager.defaultKeySequence: src/gui/qgsshortcutsmanager.h#L171
|
QgsShortcutsManager.defaultKeySequence: src/gui/qgsshortcutsmanager.h#L196
|
||||||
QgsShortcutsManager.objectDefaultKeySequence: src/gui/qgsshortcutsmanager.h#L155
|
QgsShortcutsManager.initializeCommonAction: src/gui/qgsshortcutsmanager.h#L119
|
||||||
QgsShortcutsManager.objectForSequence: src/gui/qgsshortcutsmanager.h#L213
|
QgsShortcutsManager.objectDefaultKeySequence: src/gui/qgsshortcutsmanager.h#L180
|
||||||
QgsShortcutsManager.objectForSettingKey: src/gui/qgsshortcutsmanager.h#L262
|
QgsShortcutsManager.objectForSequence: src/gui/qgsshortcutsmanager.h#L238
|
||||||
QgsShortcutsManager.objectSettingKey: src/gui/qgsshortcutsmanager.h#L254
|
QgsShortcutsManager.objectForSettingKey: src/gui/qgsshortcutsmanager.h#L293
|
||||||
QgsShortcutsManager.registerAction: src/gui/qgsshortcutsmanager.h#L94
|
QgsShortcutsManager.objectSettingKey: src/gui/qgsshortcutsmanager.h#L285
|
||||||
QgsShortcutsManager.registerAllChildActions: src/gui/qgsshortcutsmanager.h#L70
|
QgsShortcutsManager.registerAction: src/gui/qgsshortcutsmanager.h#L109
|
||||||
QgsShortcutsManager.registerAllChildShortcuts: src/gui/qgsshortcutsmanager.h#L81
|
QgsShortcutsManager.registerAllChildActions: src/gui/qgsshortcutsmanager.h#L85
|
||||||
QgsShortcutsManager.registerAllChildren: src/gui/qgsshortcutsmanager.h#L59
|
QgsShortcutsManager.registerAllChildShortcuts: src/gui/qgsshortcutsmanager.h#L96
|
||||||
QgsShortcutsManager.registerShortcut: src/gui/qgsshortcutsmanager.h#L106
|
QgsShortcutsManager.registerAllChildren: src/gui/qgsshortcutsmanager.h#L74
|
||||||
QgsShortcutsManager.setKeySequence: src/gui/qgsshortcutsmanager.h#L180
|
QgsShortcutsManager.registerShortcut: src/gui/qgsshortcutsmanager.h#L131
|
||||||
QgsShortcutsManager.setKeySequence: src/gui/qgsshortcutsmanager.h#L196
|
QgsShortcutsManager.sequenceForCommonAction: src/gui/qgsshortcutsmanager.h#L260
|
||||||
QgsShortcutsManager.setKeySequence: src/gui/qgsshortcutsmanager.h#L204
|
QgsShortcutsManager.setKeySequence: src/gui/qgsshortcutsmanager.h#L205
|
||||||
QgsShortcutsManager.setObjectKeySequence: src/gui/qgsshortcutsmanager.h#L188
|
QgsShortcutsManager.setKeySequence: src/gui/qgsshortcutsmanager.h#L221
|
||||||
QgsShortcutsManager.settingsPath: src/gui/qgsshortcutsmanager.h#L246
|
QgsShortcutsManager.setKeySequence: src/gui/qgsshortcutsmanager.h#L229
|
||||||
QgsShortcutsManager.shortcutByName: src/gui/qgsshortcutsmanager.h#L243
|
QgsShortcutsManager.setObjectKeySequence: src/gui/qgsshortcutsmanager.h#L213
|
||||||
QgsShortcutsManager.shortcutForSequence: src/gui/qgsshortcutsmanager.h#L229
|
QgsShortcutsManager.settingsPath: src/gui/qgsshortcutsmanager.h#L277
|
||||||
QgsShortcutsManager.unregisterAction: src/gui/qgsshortcutsmanager.h#L116
|
QgsShortcutsManager.shortcutByName: src/gui/qgsshortcutsmanager.h#L274
|
||||||
QgsShortcutsManager.unregisterShortcut: src/gui/qgsshortcutsmanager.h#L126
|
QgsShortcutsManager.shortcutForSequence: src/gui/qgsshortcutsmanager.h#L254
|
||||||
|
QgsShortcutsManager.unregisterAction: src/gui/qgsshortcutsmanager.h#L141
|
||||||
|
QgsShortcutsManager.unregisterShortcut: src/gui/qgsshortcutsmanager.h#L151
|
||||||
QgsShortcutsManager: src/gui/qgsshortcutsmanager.h#L36
|
QgsShortcutsManager: src/gui/qgsshortcutsmanager.h#L36
|
||||||
QgsSimpleFillSymbolLayerWidget.create: src/gui/symbology/qgssymbollayerwidget.h#L264
|
QgsSimpleFillSymbolLayerWidget.create: src/gui/symbology/qgssymbollayerwidget.h#L264
|
||||||
QgsSimpleFillSymbolLayerWidget.setColor: src/gui/symbology/qgssymbollayerwidget.h#L271
|
QgsSimpleFillSymbolLayerWidget.setColor: src/gui/symbology/qgssymbollayerwidget.h#L271
|
||||||
|
@ -19,5 +19,4 @@ __author__ = "Salvatore Larosa"
|
|||||||
__date__ = "September 2012"
|
__date__ = "September 2012"
|
||||||
__copyright__ = "(C) 2012, Salvatore Larosa"
|
__copyright__ = "(C) 2012, Salvatore Larosa"
|
||||||
|
|
||||||
from .console import show_console # NOQA
|
from .console import show_console, init_console # NOQA
|
||||||
from .console import init_options_widget
|
|
||||||
|
@ -61,6 +61,7 @@ from qgis.gui import (
|
|||||||
QgsGui,
|
QgsGui,
|
||||||
QgsApplicationExitBlockerInterface,
|
QgsApplicationExitBlockerInterface,
|
||||||
QgsCodeEditorDockWidget,
|
QgsCodeEditorDockWidget,
|
||||||
|
QgsShortcutsManager,
|
||||||
)
|
)
|
||||||
from functools import partial
|
from functools import partial
|
||||||
|
|
||||||
@ -106,8 +107,11 @@ def console_displayhook(obj):
|
|||||||
_console_output = obj
|
_console_output = obj
|
||||||
|
|
||||||
|
|
||||||
def init_options_widget():
|
def init_console():
|
||||||
"""called from QGIS to add the console options widget"""
|
"""
|
||||||
|
Called from QGIS to initialize the console related options and shortcuts,
|
||||||
|
before the dock is shown
|
||||||
|
"""
|
||||||
global _options_factory
|
global _options_factory
|
||||||
_options_factory.setTitle(QCoreApplication.translate("PythonConsole", "Python"))
|
_options_factory.setTitle(QCoreApplication.translate("PythonConsole", "Python"))
|
||||||
iface.registerOptionsWidgetFactory(_options_factory)
|
iface.registerOptionsWidgetFactory(_options_factory)
|
||||||
@ -221,145 +225,137 @@ class PythonConsoleWidget(QWidget):
|
|||||||
|
|
||||||
# Action for Open File
|
# Action for Open File
|
||||||
openFileBt = QCoreApplication.translate("PythonConsole", "Open Script…")
|
openFileBt = QCoreApplication.translate("PythonConsole", "Open Script…")
|
||||||
self.openFileButton = QAction(self)
|
self.open_file_action = QAction(self)
|
||||||
self.openFileButton.setCheckable(False)
|
self.open_file_action.setIcon(
|
||||||
self.openFileButton.setEnabled(True)
|
|
||||||
self.openFileButton.setIcon(
|
|
||||||
QgsApplication.getThemeIcon("mActionScriptOpen.svg")
|
QgsApplication.getThemeIcon("mActionScriptOpen.svg")
|
||||||
)
|
)
|
||||||
self.openFileButton.setMenuRole(QAction.MenuRole.PreferencesRole)
|
self.open_file_action.setMenuRole(QAction.MenuRole.PreferencesRole)
|
||||||
self.openFileButton.setIconVisibleInMenu(True)
|
self.open_file_action.setIconVisibleInMenu(True)
|
||||||
self.openFileButton.setToolTip(openFileBt)
|
self.open_file_action.setToolTip(f"<b>{openFileBt}</b> (Ctrl+O)")
|
||||||
self.openFileButton.setText(openFileBt)
|
self.open_file_action.setText(openFileBt)
|
||||||
|
|
||||||
openExtEditorBt = QCoreApplication.translate(
|
openExtEditorBt = QCoreApplication.translate(
|
||||||
"PythonConsole", "Open in External Editor"
|
"PythonConsole", "Open in External Editor"
|
||||||
)
|
)
|
||||||
self.openInEditorButton = QAction(self)
|
self.open_in_editor_action = QAction(self)
|
||||||
self.openInEditorButton.setCheckable(False)
|
self.open_in_editor_action.setIcon(
|
||||||
self.openInEditorButton.setEnabled(True)
|
|
||||||
self.openInEditorButton.setIcon(
|
|
||||||
QgsApplication.getThemeIcon("console/iconShowEditorConsole.svg")
|
QgsApplication.getThemeIcon("console/iconShowEditorConsole.svg")
|
||||||
)
|
)
|
||||||
self.openInEditorButton.setMenuRole(QAction.MenuRole.PreferencesRole)
|
self.open_in_editor_action.setMenuRole(QAction.MenuRole.PreferencesRole)
|
||||||
self.openInEditorButton.setIconVisibleInMenu(True)
|
self.open_in_editor_action.setIconVisibleInMenu(True)
|
||||||
self.openInEditorButton.setToolTip(openExtEditorBt)
|
self.open_in_editor_action.setToolTip(openExtEditorBt)
|
||||||
self.openInEditorButton.setText(openExtEditorBt)
|
self.open_in_editor_action.setText(openExtEditorBt)
|
||||||
|
|
||||||
# Action for Save File
|
# Action for Save File
|
||||||
saveFileBt = QCoreApplication.translate("PythonConsole", "Save")
|
saveFileBt = QCoreApplication.translate("PythonConsole", "Save")
|
||||||
self.saveFileButton = QAction(self)
|
self.save_file_action = QAction(self)
|
||||||
self.saveFileButton.setCheckable(False)
|
self.save_file_action.setEnabled(False)
|
||||||
self.saveFileButton.setEnabled(False)
|
self.save_file_action.setIcon(
|
||||||
self.saveFileButton.setIcon(QgsApplication.getThemeIcon("mActionFileSave.svg"))
|
QgsApplication.getThemeIcon("mActionFileSave.svg")
|
||||||
self.saveFileButton.setMenuRole(QAction.MenuRole.PreferencesRole)
|
)
|
||||||
self.saveFileButton.setIconVisibleInMenu(True)
|
self.save_file_action.setMenuRole(QAction.MenuRole.PreferencesRole)
|
||||||
self.saveFileButton.setToolTip(saveFileBt)
|
self.save_file_action.setIconVisibleInMenu(True)
|
||||||
self.saveFileButton.setText(saveFileBt)
|
self.save_file_action.setToolTip(f"<b>{saveFileBt}</b> (Ctrl+S)")
|
||||||
|
self.save_file_action.setText(saveFileBt)
|
||||||
|
|
||||||
# Action for Save File As
|
# Action for Save File As
|
||||||
saveAsFileBt = QCoreApplication.translate("PythonConsole", "Save As…")
|
saveAsFileBt = QCoreApplication.translate("PythonConsole", "Save As…")
|
||||||
self.saveAsFileButton = QAction(self)
|
self.save_as_file_action = QAction(self)
|
||||||
self.saveAsFileButton.setCheckable(False)
|
self.save_as_file_action.setIcon(
|
||||||
self.saveAsFileButton.setEnabled(True)
|
|
||||||
self.saveAsFileButton.setIcon(
|
|
||||||
QgsApplication.getThemeIcon("mActionFileSaveAs.svg")
|
QgsApplication.getThemeIcon("mActionFileSaveAs.svg")
|
||||||
)
|
)
|
||||||
self.saveAsFileButton.setMenuRole(QAction.MenuRole.PreferencesRole)
|
self.save_as_file_action.setMenuRole(QAction.MenuRole.PreferencesRole)
|
||||||
self.saveAsFileButton.setIconVisibleInMenu(True)
|
self.save_as_file_action.setIconVisibleInMenu(True)
|
||||||
self.saveAsFileButton.setToolTip(saveAsFileBt)
|
self.save_as_file_action.setToolTip(f"<b>{saveAsFileBt}</b> (Ctrl+Shift+S)")
|
||||||
self.saveAsFileButton.setText(saveAsFileBt)
|
self.save_as_file_action.setText(saveAsFileBt)
|
||||||
|
|
||||||
# Action Cut
|
# Action Cut
|
||||||
cutEditorBt = QCoreApplication.translate("PythonConsole", "Cut")
|
cutEditorBt = QCoreApplication.translate("PythonConsole", "Cut")
|
||||||
self.cutEditorButton = QAction(self)
|
self.cut_action = QAction(self)
|
||||||
self.cutEditorButton.setCheckable(False)
|
self.cut_action.setIcon(QgsApplication.getThemeIcon("mActionEditCut.svg"))
|
||||||
self.cutEditorButton.setEnabled(True)
|
self.cut_action.setMenuRole(QAction.MenuRole.PreferencesRole)
|
||||||
self.cutEditorButton.setIcon(QgsApplication.getThemeIcon("mActionEditCut.svg"))
|
self.cut_action.setIconVisibleInMenu(True)
|
||||||
self.cutEditorButton.setMenuRole(QAction.MenuRole.PreferencesRole)
|
self.cut_action.setToolTip(f"<b>{cutEditorBt}</b> (Ctrl+X)")
|
||||||
self.cutEditorButton.setIconVisibleInMenu(True)
|
self.cut_action.setText(cutEditorBt)
|
||||||
self.cutEditorButton.setToolTip(cutEditorBt)
|
|
||||||
self.cutEditorButton.setText(cutEditorBt)
|
|
||||||
# Action Copy
|
# Action Copy
|
||||||
copyEditorBt = QCoreApplication.translate("PythonConsole", "Copy")
|
copyEditorBt = QCoreApplication.translate("PythonConsole", "Copy")
|
||||||
self.copyEditorButton = QAction(self)
|
self.copy_action = QAction(self)
|
||||||
self.copyEditorButton.setCheckable(False)
|
self.copy_action.setIcon(QgsApplication.getThemeIcon("mActionEditCopy.svg"))
|
||||||
self.copyEditorButton.setEnabled(True)
|
self.copy_action.setMenuRole(QAction.MenuRole.PreferencesRole)
|
||||||
self.copyEditorButton.setIcon(
|
self.copy_action.setIconVisibleInMenu(True)
|
||||||
QgsApplication.getThemeIcon("mActionEditCopy.svg")
|
self.copy_action.setToolTip(f"<b>{copyEditorBt}</b> (Ctrl+C)")
|
||||||
)
|
self.copy_action.setText(copyEditorBt)
|
||||||
self.copyEditorButton.setMenuRole(QAction.MenuRole.PreferencesRole)
|
|
||||||
self.copyEditorButton.setIconVisibleInMenu(True)
|
|
||||||
self.copyEditorButton.setToolTip(copyEditorBt)
|
|
||||||
self.copyEditorButton.setText(copyEditorBt)
|
|
||||||
# Action Paste
|
# Action Paste
|
||||||
pasteEditorBt = QCoreApplication.translate("PythonConsole", "Paste")
|
pasteEditorBt = QCoreApplication.translate("PythonConsole", "Paste")
|
||||||
self.pasteEditorButton = QAction(self)
|
self.paste_action = QAction(self)
|
||||||
self.pasteEditorButton.setCheckable(False)
|
self.paste_action.setIcon(QgsApplication.getThemeIcon("mActionEditPaste.svg"))
|
||||||
self.pasteEditorButton.setEnabled(True)
|
self.paste_action.setMenuRole(QAction.MenuRole.PreferencesRole)
|
||||||
self.pasteEditorButton.setIcon(
|
self.paste_action.setIconVisibleInMenu(True)
|
||||||
QgsApplication.getThemeIcon("mActionEditPaste.svg")
|
self.paste_action.setToolTip(f"<b>{pasteEditorBt}</b> (Ctrl+V)")
|
||||||
)
|
self.paste_action.setText(pasteEditorBt)
|
||||||
self.pasteEditorButton.setMenuRole(QAction.MenuRole.PreferencesRole)
|
|
||||||
self.pasteEditorButton.setIconVisibleInMenu(True)
|
|
||||||
self.pasteEditorButton.setToolTip(pasteEditorBt)
|
|
||||||
self.pasteEditorButton.setText(pasteEditorBt)
|
|
||||||
# Action Run Script (subprocess)
|
# Action Run Script (subprocess)
|
||||||
runScriptEditorBt = QCoreApplication.translate("PythonConsole", "Run Script")
|
self.run_script_action = QAction(self)
|
||||||
self.runScriptEditorButton = QAction(self)
|
self.run_script_action.setIcon(QgsApplication.getThemeIcon("mActionStart.svg"))
|
||||||
self.runScriptEditorButton.setCheckable(False)
|
self.run_script_action.setMenuRole(QAction.MenuRole.PreferencesRole)
|
||||||
self.runScriptEditorButton.setEnabled(True)
|
self.run_script_action.setIconVisibleInMenu(True)
|
||||||
self.runScriptEditorButton.setIcon(
|
QgsGui.shortcutsManager().initializeCommonAction(
|
||||||
QgsApplication.getThemeIcon("mActionStart.svg")
|
self.run_script_action, QgsShortcutsManager.CommonAction.CodeRunScript
|
||||||
|
)
|
||||||
|
|
||||||
|
# Action Run Selected
|
||||||
|
self.run_selection_action = QAction(self)
|
||||||
|
self.run_selection_action.setIcon(
|
||||||
|
QgsApplication.getThemeIcon("mActionRunSelected.svg")
|
||||||
|
)
|
||||||
|
self.run_selection_action.setMenuRole(QAction.MenuRole.PreferencesRole)
|
||||||
|
self.run_selection_action.setIconVisibleInMenu(True)
|
||||||
|
QgsGui.shortcutsManager().initializeCommonAction(
|
||||||
|
self.run_selection_action, QgsShortcutsManager.CommonAction.CodeRunSelection
|
||||||
)
|
)
|
||||||
self.runScriptEditorButton.setMenuRole(QAction.MenuRole.PreferencesRole)
|
|
||||||
self.runScriptEditorButton.setIconVisibleInMenu(True)
|
|
||||||
self.runScriptEditorButton.setToolTip(runScriptEditorBt)
|
|
||||||
self.runScriptEditorButton.setText(runScriptEditorBt)
|
|
||||||
|
|
||||||
# Action Toggle comment
|
# Action Toggle comment
|
||||||
toggleText = QCoreApplication.translate("PythonConsole", "Toggle Comment")
|
self.toggle_comment_action = QAction(self)
|
||||||
self.toggleCommentEditorButton = QAction(self)
|
self.toggle_comment_action.setIcon(
|
||||||
self.toggleCommentEditorButton.setCheckable(False)
|
|
||||||
self.toggleCommentEditorButton.setEnabled(True)
|
|
||||||
self.toggleCommentEditorButton.setIcon(
|
|
||||||
QgsApplication.getThemeIcon(
|
QgsApplication.getThemeIcon(
|
||||||
"console/iconCommentEditorConsole.svg",
|
"console/iconCommentEditorConsole.svg",
|
||||||
self.palette().color(QPalette.ColorRole.WindowText),
|
self.palette().color(QPalette.ColorRole.WindowText),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
self.toggleCommentEditorButton.setMenuRole(QAction.MenuRole.PreferencesRole)
|
self.toggle_comment_action.setMenuRole(QAction.MenuRole.PreferencesRole)
|
||||||
self.toggleCommentEditorButton.setIconVisibleInMenu(True)
|
self.toggle_comment_action.setIconVisibleInMenu(True)
|
||||||
self.toggleCommentEditorButton.setToolTip(toggleText + " <b>Ctrl+:</b>")
|
QgsGui.shortcutsManager().initializeCommonAction(
|
||||||
self.toggleCommentEditorButton.setText(toggleText)
|
self.toggle_comment_action,
|
||||||
|
QgsShortcutsManager.CommonAction.CodeToggleComment,
|
||||||
|
)
|
||||||
|
|
||||||
# Action Format code
|
# Action Format code
|
||||||
reformatCodeText = QCoreApplication.translate("PythonConsole", "Reformat Code")
|
self.reformat_code_action = QAction(self)
|
||||||
self.reformatCodeEditorButton = QAction(self)
|
self.reformat_code_action.setIcon(
|
||||||
self.reformatCodeEditorButton.setCheckable(False)
|
|
||||||
self.reformatCodeEditorButton.setEnabled(True)
|
|
||||||
self.reformatCodeEditorButton.setIcon(
|
|
||||||
QgsApplication.getThemeIcon("console/iconFormatCode.svg")
|
QgsApplication.getThemeIcon("console/iconFormatCode.svg")
|
||||||
)
|
)
|
||||||
self.reformatCodeEditorButton.setMenuRole(QAction.MenuRole.PreferencesRole)
|
self.reformat_code_action.setMenuRole(QAction.MenuRole.PreferencesRole)
|
||||||
self.reformatCodeEditorButton.setIconVisibleInMenu(True)
|
self.reformat_code_action.setIconVisibleInMenu(True)
|
||||||
self.reformatCodeEditorButton.setToolTip(
|
QgsGui.shortcutsManager().initializeCommonAction(
|
||||||
reformatCodeText + " <b>Ctrl+Alt+F</b>"
|
self.reformat_code_action, QgsShortcutsManager.CommonAction.CodeReformat
|
||||||
)
|
)
|
||||||
self.reformatCodeEditorButton.setShortcut("Ctrl+Alt+F")
|
|
||||||
self.reformatCodeEditorButton.setText(reformatCodeText)
|
|
||||||
|
|
||||||
# Action for Object browser
|
# Action for Object browser
|
||||||
objList = QCoreApplication.translate("PythonConsole", "Object Inspector…")
|
objList = QCoreApplication.translate("PythonConsole", "Object Inspector…")
|
||||||
self.objectListButton = QAction(self)
|
self.object_inspector_action = QAction(self)
|
||||||
self.objectListButton.setCheckable(True)
|
self.object_inspector_action.setCheckable(True)
|
||||||
self.objectListButton.setEnabled(
|
self.object_inspector_action.setEnabled(
|
||||||
QgsSettings().value("pythonConsole/enableObjectInsp", False, type=bool)
|
QgsSettings().value("pythonConsole/enableObjectInsp", False, type=bool)
|
||||||
)
|
)
|
||||||
self.objectListButton.setIcon(
|
self.object_inspector_action.setIcon(
|
||||||
QgsApplication.getThemeIcon("console/iconClassBrowserConsole.svg")
|
QgsApplication.getThemeIcon("console/iconClassBrowserConsole.svg")
|
||||||
)
|
)
|
||||||
self.objectListButton.setMenuRole(QAction.MenuRole.PreferencesRole)
|
self.object_inspector_action.setMenuRole(QAction.MenuRole.PreferencesRole)
|
||||||
self.objectListButton.setIconVisibleInMenu(True)
|
self.object_inspector_action.setIconVisibleInMenu(True)
|
||||||
self.objectListButton.setToolTip(objList)
|
self.object_inspector_action.setToolTip(objList)
|
||||||
self.objectListButton.setText(objList)
|
self.object_inspector_action.setText(objList)
|
||||||
|
|
||||||
# Action for Find text
|
# Action for Find text
|
||||||
findText = QCoreApplication.translate("PythonConsole", "Find Text")
|
findText = QCoreApplication.translate("PythonConsole", "Find Text")
|
||||||
@ -371,7 +367,7 @@ class PythonConsoleWidget(QWidget):
|
|||||||
)
|
)
|
||||||
self.find_text_action.setMenuRole(QAction.MenuRole.PreferencesRole)
|
self.find_text_action.setMenuRole(QAction.MenuRole.PreferencesRole)
|
||||||
self.find_text_action.setIconVisibleInMenu(True)
|
self.find_text_action.setIconVisibleInMenu(True)
|
||||||
self.find_text_action.setToolTip(findText)
|
self.find_text_action.setToolTip(f"<b>{findText}</b> (Ctrl+F)")
|
||||||
self.find_text_action.setText(findText)
|
self.find_text_action.setText(findText)
|
||||||
|
|
||||||
self.tabEditorWidget.search_bar_toggled.connect(
|
self.tabEditorWidget.search_bar_toggled.connect(
|
||||||
@ -383,72 +379,65 @@ class PythonConsoleWidget(QWidget):
|
|||||||
|
|
||||||
# Action Show Editor
|
# Action Show Editor
|
||||||
showEditor = QCoreApplication.translate("PythonConsole", "Show Editor")
|
showEditor = QCoreApplication.translate("PythonConsole", "Show Editor")
|
||||||
self.showEditorButton = QAction(self)
|
self.show_editor_action = QAction(self)
|
||||||
self.showEditorButton.setEnabled(True)
|
self.show_editor_action.setCheckable(True)
|
||||||
self.showEditorButton.setCheckable(True)
|
self.show_editor_action.setIcon(
|
||||||
self.showEditorButton.setIcon(
|
|
||||||
QgsApplication.getThemeIcon("console/iconShowEditorConsole.svg")
|
QgsApplication.getThemeIcon("console/iconShowEditorConsole.svg")
|
||||||
)
|
)
|
||||||
self.showEditorButton.setMenuRole(QAction.MenuRole.PreferencesRole)
|
self.show_editor_action.setMenuRole(QAction.MenuRole.PreferencesRole)
|
||||||
self.showEditorButton.setIconVisibleInMenu(True)
|
self.show_editor_action.setIconVisibleInMenu(True)
|
||||||
self.showEditorButton.setToolTip(showEditor)
|
self.show_editor_action.setToolTip(showEditor)
|
||||||
self.showEditorButton.setText(showEditor)
|
self.show_editor_action.setText(showEditor)
|
||||||
|
|
||||||
# Action for Clear button
|
# Action for Clear button
|
||||||
clearBt = QCoreApplication.translate("PythonConsole", "Clear Console")
|
clearBt = QCoreApplication.translate("PythonConsole", "Clear Console")
|
||||||
self.clearButton = QAction(self)
|
self.clear_action = QAction(self)
|
||||||
self.clearButton.setCheckable(False)
|
self.clear_action.setIcon(
|
||||||
self.clearButton.setEnabled(True)
|
|
||||||
self.clearButton.setIcon(
|
|
||||||
QgsApplication.getThemeIcon("console/iconClearConsole.svg")
|
QgsApplication.getThemeIcon("console/iconClearConsole.svg")
|
||||||
)
|
)
|
||||||
self.clearButton.setMenuRole(QAction.MenuRole.PreferencesRole)
|
self.clear_action.setMenuRole(QAction.MenuRole.PreferencesRole)
|
||||||
self.clearButton.setIconVisibleInMenu(True)
|
self.clear_action.setIconVisibleInMenu(True)
|
||||||
self.clearButton.setToolTip(clearBt)
|
self.clear_action.setToolTip(clearBt)
|
||||||
self.clearButton.setText(clearBt)
|
self.clear_action.setText(clearBt)
|
||||||
|
|
||||||
# Action for settings
|
# Action for settings
|
||||||
optionsBt = QCoreApplication.translate("PythonConsole", "Options…")
|
optionsBt = QCoreApplication.translate("PythonConsole", "Options…")
|
||||||
self.optionsButton = QAction(self)
|
self.options_action = QAction(self)
|
||||||
self.optionsButton.setCheckable(False)
|
self.options_action.setIcon(
|
||||||
self.optionsButton.setEnabled(True)
|
|
||||||
self.optionsButton.setIcon(
|
|
||||||
QgsApplication.getThemeIcon("console/iconSettingsConsole.svg")
|
QgsApplication.getThemeIcon("console/iconSettingsConsole.svg")
|
||||||
)
|
)
|
||||||
self.optionsButton.setMenuRole(QAction.MenuRole.PreferencesRole)
|
self.options_action.setMenuRole(QAction.MenuRole.PreferencesRole)
|
||||||
self.optionsButton.setIconVisibleInMenu(True)
|
self.options_action.setIconVisibleInMenu(True)
|
||||||
self.optionsButton.setToolTip(optionsBt)
|
self.options_action.setToolTip(optionsBt)
|
||||||
self.optionsButton.setText(optionsBt)
|
self.options_action.setText(optionsBt)
|
||||||
|
|
||||||
# Action for Run script
|
# Action for Run script
|
||||||
runBt = QCoreApplication.translate("PythonConsole", "Run Command")
|
runBt = QCoreApplication.translate("PythonConsole", "Run Command")
|
||||||
self.runButton = QAction(self)
|
self.run_action = QAction(self)
|
||||||
self.runButton.setCheckable(False)
|
self.run_action.setIcon(QgsApplication.getThemeIcon("mActionStart.svg"))
|
||||||
self.runButton.setEnabled(True)
|
self.run_action.setMenuRole(QAction.MenuRole.PreferencesRole)
|
||||||
self.runButton.setIcon(QgsApplication.getThemeIcon("mActionStart.svg"))
|
self.run_action.setIconVisibleInMenu(True)
|
||||||
self.runButton.setMenuRole(QAction.MenuRole.PreferencesRole)
|
self.run_action.setToolTip(runBt)
|
||||||
self.runButton.setIconVisibleInMenu(True)
|
self.run_action.setText(runBt)
|
||||||
self.runButton.setToolTip(runBt)
|
|
||||||
self.runButton.setText(runBt)
|
|
||||||
|
|
||||||
# Help button
|
# Help button
|
||||||
self.helpConsoleAction = QAction(self)
|
self.help_console_action = QAction(self)
|
||||||
self.helpConsoleAction.setEnabled(True)
|
self.help_console_action.setText(
|
||||||
self.helpConsoleAction.setText(
|
|
||||||
QCoreApplication.translate("PythonConsole", "Python Console Help")
|
QCoreApplication.translate("PythonConsole", "Python Console Help")
|
||||||
)
|
)
|
||||||
self.helpAPIAction = QAction(self)
|
self.help_api_action = QAction(self)
|
||||||
self.helpAPIAction.setEnabled(True)
|
self.help_api_action.setText(
|
||||||
self.helpAPIAction.setText(
|
|
||||||
QCoreApplication.translate("PythonConsole", "PyQGIS API Documentation")
|
QCoreApplication.translate("PythonConsole", "PyQGIS API Documentation")
|
||||||
)
|
)
|
||||||
self.helpCookbookAction = QAction(self)
|
self.help_cookbook_action = QAction(self)
|
||||||
self.helpCookbookAction.setEnabled(True)
|
self.help_cookbook_action.setText(
|
||||||
self.helpCookbookAction.setText(
|
|
||||||
QCoreApplication.translate("PythonConsole", "PyQGIS Cookbook")
|
QCoreApplication.translate("PythonConsole", "PyQGIS Cookbook")
|
||||||
)
|
)
|
||||||
|
|
||||||
self.helpMenu = QMenu(self)
|
self.helpMenu = QMenu(self)
|
||||||
self.helpMenu.addAction(self.helpConsoleAction)
|
self.helpMenu.addAction(self.help_console_action)
|
||||||
self.helpMenu.addAction(self.helpAPIAction)
|
self.helpMenu.addAction(self.help_api_action)
|
||||||
self.helpMenu.addAction(self.helpCookbookAction)
|
self.helpMenu.addAction(self.help_cookbook_action)
|
||||||
|
|
||||||
helpBt = QCoreApplication.translate("PythonConsole", "Help…")
|
helpBt = QCoreApplication.translate("PythonConsole", "Help…")
|
||||||
self.helpButton = QToolButton(self)
|
self.helpButton = QToolButton(self)
|
||||||
@ -468,12 +457,12 @@ class PythonConsoleWidget(QWidget):
|
|||||||
self.toolBar.setIconSize(icon_size)
|
self.toolBar.setIconSize(icon_size)
|
||||||
self.toolBar.setMovable(False)
|
self.toolBar.setMovable(False)
|
||||||
self.toolBar.setFloatable(False)
|
self.toolBar.setFloatable(False)
|
||||||
self.toolBar.addAction(self.clearButton)
|
self.toolBar.addAction(self.clear_action)
|
||||||
self.toolBar.addAction(self.runButton)
|
self.toolBar.addAction(self.run_action)
|
||||||
self.toolBar.addSeparator()
|
self.toolBar.addSeparator()
|
||||||
self.toolBar.addAction(self.showEditorButton)
|
self.toolBar.addAction(self.show_editor_action)
|
||||||
self.toolBar.addSeparator()
|
self.toolBar.addSeparator()
|
||||||
self.toolBar.addAction(self.optionsButton)
|
self.toolBar.addAction(self.options_action)
|
||||||
self.toolBar.addWidget(self.helpButton)
|
self.toolBar.addWidget(self.helpButton)
|
||||||
self.toolBar.addSeparator()
|
self.toolBar.addSeparator()
|
||||||
self.toolBar.addWidget(parent.dockToggleButton())
|
self.toolBar.addWidget(parent.dockToggleButton())
|
||||||
@ -486,24 +475,25 @@ class PythonConsoleWidget(QWidget):
|
|||||||
self.toolBarEditor.setIconSize(icon_size)
|
self.toolBarEditor.setIconSize(icon_size)
|
||||||
self.toolBarEditor.setMovable(False)
|
self.toolBarEditor.setMovable(False)
|
||||||
self.toolBarEditor.setFloatable(False)
|
self.toolBarEditor.setFloatable(False)
|
||||||
self.toolBarEditor.addAction(self.openFileButton)
|
self.toolBarEditor.addAction(self.open_file_action)
|
||||||
self.toolBarEditor.addAction(self.openInEditorButton)
|
self.toolBarEditor.addAction(self.open_in_editor_action)
|
||||||
self.toolBarEditor.addSeparator()
|
self.toolBarEditor.addSeparator()
|
||||||
self.toolBarEditor.addAction(self.saveFileButton)
|
self.toolBarEditor.addAction(self.save_file_action)
|
||||||
self.toolBarEditor.addAction(self.saveAsFileButton)
|
self.toolBarEditor.addAction(self.save_as_file_action)
|
||||||
self.toolBarEditor.addSeparator()
|
self.toolBarEditor.addSeparator()
|
||||||
self.toolBarEditor.addAction(self.runScriptEditorButton)
|
self.toolBarEditor.addAction(self.run_script_action)
|
||||||
|
self.toolBarEditor.addAction(self.run_selection_action)
|
||||||
self.toolBarEditor.addSeparator()
|
self.toolBarEditor.addSeparator()
|
||||||
self.toolBarEditor.addAction(self.cutEditorButton)
|
self.toolBarEditor.addAction(self.cut_action)
|
||||||
self.toolBarEditor.addAction(self.copyEditorButton)
|
self.toolBarEditor.addAction(self.copy_action)
|
||||||
self.toolBarEditor.addAction(self.pasteEditorButton)
|
self.toolBarEditor.addAction(self.paste_action)
|
||||||
self.toolBarEditor.addSeparator()
|
self.toolBarEditor.addSeparator()
|
||||||
self.toolBarEditor.addAction(self.find_text_action)
|
self.toolBarEditor.addAction(self.find_text_action)
|
||||||
self.toolBarEditor.addSeparator()
|
self.toolBarEditor.addSeparator()
|
||||||
self.toolBarEditor.addAction(self.toggleCommentEditorButton)
|
self.toolBarEditor.addAction(self.toggle_comment_action)
|
||||||
self.toolBarEditor.addAction(self.reformatCodeEditorButton)
|
self.toolBarEditor.addAction(self.reformat_code_action)
|
||||||
self.toolBarEditor.addSeparator()
|
self.toolBarEditor.addSeparator()
|
||||||
self.toolBarEditor.addAction(self.objectListButton)
|
self.toolBarEditor.addAction(self.object_inspector_action)
|
||||||
|
|
||||||
self.widgetButton = QWidget()
|
self.widgetButton = QWidget()
|
||||||
sizePolicy = QSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Preferred)
|
sizePolicy = QSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Preferred)
|
||||||
@ -557,24 +547,25 @@ class PythonConsoleWidget(QWidget):
|
|||||||
|
|
||||||
# ------------ Signal -------------------------------
|
# ------------ Signal -------------------------------
|
||||||
|
|
||||||
self.objectListButton.toggled.connect(self.toggleObjectListWidget)
|
self.object_inspector_action.toggled.connect(self.toggleObjectListWidget)
|
||||||
self.toggleCommentEditorButton.triggered.connect(self.toggleComment)
|
self.toggle_comment_action.triggered.connect(self.toggleComment)
|
||||||
self.reformatCodeEditorButton.triggered.connect(self.reformatCode)
|
self.reformat_code_action.triggered.connect(self.reformatCode)
|
||||||
self.runScriptEditorButton.triggered.connect(self.runScriptEditor)
|
self.run_script_action.triggered.connect(self.runScriptEditor)
|
||||||
self.cutEditorButton.triggered.connect(self.cutEditor)
|
self.run_selection_action.triggered.connect(self.runSelectedEditor)
|
||||||
self.copyEditorButton.triggered.connect(self.copyEditor)
|
self.cut_action.triggered.connect(self.cutEditor)
|
||||||
self.pasteEditorButton.triggered.connect(self.pasteEditor)
|
self.copy_action.triggered.connect(self.copyEditor)
|
||||||
self.showEditorButton.toggled.connect(self.toggleEditor)
|
self.paste_action.triggered.connect(self.pasteEditor)
|
||||||
self.clearButton.triggered.connect(self.shell_output.clearConsole)
|
self.show_editor_action.toggled.connect(self.toggleEditor)
|
||||||
self.optionsButton.triggered.connect(self.openSettings)
|
self.clear_action.triggered.connect(self.shell_output.clearConsole)
|
||||||
self.runButton.triggered.connect(self.shell.entered)
|
self.options_action.triggered.connect(self.openSettings)
|
||||||
self.openFileButton.triggered.connect(self.openScriptFile)
|
self.run_action.triggered.connect(self.shell.entered)
|
||||||
self.openInEditorButton.triggered.connect(self.openScriptFileExtEditor)
|
self.open_file_action.triggered.connect(self.openScriptFile)
|
||||||
self.saveFileButton.triggered.connect(self.saveScriptFile)
|
self.open_in_editor_action.triggered.connect(self.openScriptFileExtEditor)
|
||||||
self.saveAsFileButton.triggered.connect(self.saveAsScriptFile)
|
self.save_file_action.triggered.connect(self.saveScriptFile)
|
||||||
self.helpConsoleAction.triggered.connect(self.openHelpConsole)
|
self.save_as_file_action.triggered.connect(self.saveAsScriptFile)
|
||||||
self.helpAPIAction.triggered.connect(self.openHelpAPI)
|
self.help_console_action.triggered.connect(self.openHelpConsole)
|
||||||
self.helpCookbookAction.triggered.connect(self.openHelpCookbook)
|
self.help_api_action.triggered.connect(self.openHelpAPI)
|
||||||
|
self.help_cookbook_action.triggered.connect(self.openHelpCookbook)
|
||||||
self.listClassMethod.itemClicked.connect(self.onClickGoToLine)
|
self.listClassMethod.itemClicked.connect(self.onClickGoToLine)
|
||||||
|
|
||||||
if iface is not None:
|
if iface is not None:
|
||||||
@ -653,6 +644,9 @@ class PythonConsoleWidget(QWidget):
|
|||||||
def runScriptEditor(self):
|
def runScriptEditor(self):
|
||||||
self.tabEditorWidget.currentWidget().runScriptCode()
|
self.tabEditorWidget.currentWidget().runScriptCode()
|
||||||
|
|
||||||
|
def runSelectedEditor(self):
|
||||||
|
self.tabEditorWidget.currentWidget().runSelectedCode()
|
||||||
|
|
||||||
def toggleComment(self):
|
def toggleComment(self):
|
||||||
self.tabEditorWidget.currentWidget().toggleComment()
|
self.tabEditorWidget.currentWidget().toggleComment()
|
||||||
|
|
||||||
|
@ -33,7 +33,13 @@ from operator import itemgetter
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from qgis.core import Qgis, QgsApplication, QgsBlockingNetworkRequest, QgsSettings
|
from qgis.core import Qgis, QgsApplication, QgsBlockingNetworkRequest, QgsSettings
|
||||||
from qgis.gui import QgsCodeEditorPython, QgsCodeEditorWidget, QgsMessageBar
|
from qgis.gui import (
|
||||||
|
QgsCodeEditorPython,
|
||||||
|
QgsCodeEditorWidget,
|
||||||
|
QgsGui,
|
||||||
|
QgsMessageBar,
|
||||||
|
QgsShortcutsManager,
|
||||||
|
)
|
||||||
|
|
||||||
from qgis.PyQt.Qsci import QsciScintilla
|
from qgis.PyQt.Qsci import QsciScintilla
|
||||||
from qgis.PyQt.QtCore import (
|
from qgis.PyQt.QtCore import (
|
||||||
@ -116,9 +122,6 @@ class Editor(QgsCodeEditorPython):
|
|||||||
self.redoScut.setContext(Qt.ShortcutContext.WidgetShortcut)
|
self.redoScut.setContext(Qt.ShortcutContext.WidgetShortcut)
|
||||||
self.redoScut.activated.connect(self.redo)
|
self.redoScut.activated.connect(self.redo)
|
||||||
self.newShortcutCS.activated.connect(self.autoComplete)
|
self.newShortcutCS.activated.connect(self.autoComplete)
|
||||||
self.runScut = QShortcut(QKeySequence(Qt.Modifier.CTRL | Qt.Key.Key_E), self)
|
|
||||||
self.runScut.setContext(Qt.ShortcutContext.WidgetShortcut)
|
|
||||||
self.runScut.activated.connect(self.runSelectedCode) # spellok
|
|
||||||
self.runScriptScut = QShortcut(
|
self.runScriptScut = QShortcut(
|
||||||
QKeySequence(Qt.Modifier.SHIFT | Qt.Modifier.CTRL | Qt.Key.Key_E), self
|
QKeySequence(Qt.Modifier.SHIFT | Qt.Modifier.CTRL | Qt.Key.Key_E), self
|
||||||
)
|
)
|
||||||
@ -162,15 +165,6 @@ class Editor(QgsCodeEditorPython):
|
|||||||
syntaxCheckAction.setShortcut("Ctrl+4")
|
syntaxCheckAction.setShortcut("Ctrl+4")
|
||||||
menu.addAction(syntaxCheckAction)
|
menu.addAction(syntaxCheckAction)
|
||||||
|
|
||||||
runSelected = QAction(
|
|
||||||
QgsApplication.getThemeIcon("console/mIconRunConsole.svg"), # spellok
|
|
||||||
QCoreApplication.translate("PythonConsole", "Run Selected"),
|
|
||||||
menu,
|
|
||||||
)
|
|
||||||
runSelected.triggered.connect(self.runSelectedCode) # spellok
|
|
||||||
runSelected.setShortcut("Ctrl+E") # spellok
|
|
||||||
menu.addAction(runSelected) # spellok
|
|
||||||
|
|
||||||
word = self.selectedText() or self.wordAtPoint(e.pos())
|
word = self.selectedText() or self.wordAtPoint(e.pos())
|
||||||
if word:
|
if word:
|
||||||
context_help_action = QAction(
|
context_help_action = QAction(
|
||||||
@ -188,13 +182,24 @@ class Editor(QgsCodeEditorPython):
|
|||||||
context_help_action.setShortcut(QKeySequence.StandardKey.HelpContents)
|
context_help_action.setShortcut(QKeySequence.StandardKey.HelpContents)
|
||||||
menu.addAction(context_help_action)
|
menu.addAction(context_help_action)
|
||||||
|
|
||||||
start_action = QAction(
|
run_selection_action = QAction(menu)
|
||||||
QgsApplication.getThemeIcon("mActionStart.svg"),
|
run_selection_action.setIcon(
|
||||||
QCoreApplication.translate("PythonConsole", "Run Script"),
|
QgsApplication.getThemeIcon("mActionRunSelected.svg"),
|
||||||
menu,
|
|
||||||
)
|
)
|
||||||
|
run_selection_action.triggered.connect(self.runSelectedCode)
|
||||||
|
QgsGui.shortcutsManager().initializeCommonAction(
|
||||||
|
run_selection_action,
|
||||||
|
QgsShortcutsManager.CommonAction.CodeRunSelection,
|
||||||
|
)
|
||||||
|
menu.addAction(run_selection_action)
|
||||||
|
|
||||||
|
start_action = QAction(self)
|
||||||
|
start_action.setIcon(QgsApplication.getThemeIcon("mActionStart.svg"))
|
||||||
start_action.triggered.connect(self.runScriptCode)
|
start_action.triggered.connect(self.runScriptCode)
|
||||||
start_action.setShortcut("Ctrl+Shift+E")
|
QgsGui.shortcutsManager().initializeCommonAction(
|
||||||
|
start_action,
|
||||||
|
QgsShortcutsManager.CommonAction.CodeRunScript,
|
||||||
|
)
|
||||||
menu.addAction(start_action)
|
menu.addAction(start_action)
|
||||||
|
|
||||||
menu.addSeparator()
|
menu.addSeparator()
|
||||||
@ -223,6 +228,7 @@ class Editor(QgsCodeEditorPython):
|
|||||||
menu,
|
menu,
|
||||||
)
|
)
|
||||||
find_action.triggered.connect(self.trigger_find)
|
find_action.triggered.connect(self.trigger_find)
|
||||||
|
find_action.setShortcut("Ctrl+F")
|
||||||
menu.addAction(find_action)
|
menu.addAction(find_action)
|
||||||
|
|
||||||
cutAction = QAction(
|
cutAction = QAction(
|
||||||
@ -260,18 +266,31 @@ class Editor(QgsCodeEditorPython):
|
|||||||
menu.addAction(selectAllAction)
|
menu.addAction(selectAllAction)
|
||||||
|
|
||||||
menu.addSeparator()
|
menu.addSeparator()
|
||||||
toggle_comment_action = QAction(
|
toggle_comment_action = QAction(menu)
|
||||||
|
toggle_comment_action.setIcon(
|
||||||
QgsApplication.getThemeIcon(
|
QgsApplication.getThemeIcon(
|
||||||
"console/iconCommentEditorConsole.svg",
|
"console/iconCommentEditorConsole.svg",
|
||||||
self.palette().color(QPalette.ColorRole.WindowText),
|
self.palette().color(QPalette.ColorRole.WindowText),
|
||||||
),
|
)
|
||||||
QCoreApplication.translate("PythonConsole", "Toggle Comment"),
|
|
||||||
menu,
|
|
||||||
)
|
)
|
||||||
toggle_comment_action.triggered.connect(self.toggleComment)
|
toggle_comment_action.triggered.connect(self.toggleComment)
|
||||||
toggle_comment_action.setShortcut("Ctrl+:")
|
QgsGui.shortcutsManager().initializeCommonAction(
|
||||||
|
toggle_comment_action,
|
||||||
|
QgsShortcutsManager.CommonAction.CodeToggleComment,
|
||||||
|
)
|
||||||
menu.addAction(toggle_comment_action)
|
menu.addAction(toggle_comment_action)
|
||||||
|
|
||||||
|
reformat_code_action = QAction(menu)
|
||||||
|
reformat_code_action.setIcon(
|
||||||
|
QgsApplication.getThemeIcon("console/iconFormatCode.svg")
|
||||||
|
)
|
||||||
|
reformat_code_action.triggered.connect(self.reformatCode)
|
||||||
|
QgsGui.shortcutsManager().initializeCommonAction(
|
||||||
|
reformat_code_action,
|
||||||
|
QgsShortcutsManager.CommonAction.CodeReformat,
|
||||||
|
)
|
||||||
|
menu.addAction(reformat_code_action)
|
||||||
|
|
||||||
menu.addSeparator()
|
menu.addSeparator()
|
||||||
gist_menu = QMenu(self)
|
gist_menu = QMenu(self)
|
||||||
gist_menu.setTitle(
|
gist_menu.setTitle(
|
||||||
@ -301,14 +320,14 @@ class Editor(QgsCodeEditorPython):
|
|||||||
syntaxCheckAction.setEnabled(False)
|
syntaxCheckAction.setEnabled(False)
|
||||||
pasteAction.setEnabled(False)
|
pasteAction.setEnabled(False)
|
||||||
cutAction.setEnabled(False)
|
cutAction.setEnabled(False)
|
||||||
runSelected.setEnabled(False) # spellok
|
run_selection_action.setEnabled(False)
|
||||||
copyAction.setEnabled(False)
|
copyAction.setEnabled(False)
|
||||||
selectAllAction.setEnabled(False)
|
selectAllAction.setEnabled(False)
|
||||||
undoAction.setEnabled(False)
|
undoAction.setEnabled(False)
|
||||||
redoAction.setEnabled(False)
|
redoAction.setEnabled(False)
|
||||||
showCodeInspection.setEnabled(False)
|
showCodeInspection.setEnabled(False)
|
||||||
if self.hasSelectedText():
|
if self.hasSelectedText():
|
||||||
runSelected.setEnabled(True) # spellok
|
run_selection_action.setEnabled(True)
|
||||||
copyAction.setEnabled(True)
|
copyAction.setEnabled(True)
|
||||||
cutAction.setEnabled(True)
|
cutAction.setEnabled(True)
|
||||||
if not self.text() == "":
|
if not self.text() == "":
|
||||||
@ -328,17 +347,17 @@ class Editor(QgsCodeEditorPython):
|
|||||||
listObj = self.console_widget.listClassMethod
|
listObj = self.console_widget.listClassMethod
|
||||||
if listObj.isVisible():
|
if listObj.isVisible():
|
||||||
listObj.hide()
|
listObj.hide()
|
||||||
self.console_widget.objectListButton.setChecked(False)
|
self.console_widget.object_inspector_action.setChecked(False)
|
||||||
else:
|
else:
|
||||||
listObj.show()
|
listObj.show()
|
||||||
self.console_widget.objectListButton.setChecked(True)
|
self.console_widget.object_inspector_action.setChecked(True)
|
||||||
|
|
||||||
def shareOnGist(self, is_public):
|
def shareOnGist(self, is_public):
|
||||||
self.code_editor_widget.shareOnGist(is_public)
|
self.code_editor_widget.shareOnGist(is_public)
|
||||||
|
|
||||||
def hideEditor(self):
|
def hideEditor(self):
|
||||||
self.console_widget.splitterObj.hide()
|
self.console_widget.splitterObj.hide()
|
||||||
self.console_widget.showEditorButton.setChecked(False)
|
self.console_widget.show_editor_action.setChecked(False)
|
||||||
|
|
||||||
def createTempFile(self):
|
def createTempFile(self):
|
||||||
name = tempfile.NamedTemporaryFile(delete=False).name
|
name = tempfile.NamedTemporaryFile(delete=False).name
|
||||||
@ -477,7 +496,7 @@ class Editor(QgsCodeEditorPython):
|
|||||||
)
|
)
|
||||||
self.tab_widget.setTabToolTip(index, self.code_editor_widget.filePath())
|
self.tab_widget.setTabToolTip(index, self.code_editor_widget.filePath())
|
||||||
self.setModified(False)
|
self.setModified(False)
|
||||||
self.console_widget.saveFileButton.setEnabled(False)
|
self.console_widget.save_file_action.setEnabled(False)
|
||||||
self.console_widget.updateTabListScript(
|
self.console_widget.updateTabListScript(
|
||||||
self.code_editor_widget.filePath(), action="append"
|
self.code_editor_widget.filePath(), action="append"
|
||||||
)
|
)
|
||||||
@ -722,7 +741,7 @@ class EditorTabWidget(QTabWidget):
|
|||||||
# New Editor button
|
# New Editor button
|
||||||
self.newTabButton = QToolButton()
|
self.newTabButton = QToolButton()
|
||||||
txtToolTipNewTab = QCoreApplication.translate("PythonConsole", "New Editor")
|
txtToolTipNewTab = QCoreApplication.translate("PythonConsole", "New Editor")
|
||||||
self.newTabButton.setToolTip(txtToolTipNewTab)
|
self.newTabButton.setToolTip(f"<b>{txtToolTipNewTab}</b> (Ctrl+T)")
|
||||||
self.newTabButton.setAutoRaise(True)
|
self.newTabButton.setAutoRaise(True)
|
||||||
self.newTabButton.setIcon(
|
self.newTabButton.setIcon(
|
||||||
QgsApplication.getThemeIcon("console/iconNewTabEditorConsole.svg")
|
QgsApplication.getThemeIcon("console/iconNewTabEditorConsole.svg")
|
||||||
@ -812,7 +831,7 @@ class EditorTabWidget(QTabWidget):
|
|||||||
def enableSaveIfModified(self, tab):
|
def enableSaveIfModified(self, tab):
|
||||||
tabWidget = self.widget(tab)
|
tabWidget = self.widget(tab)
|
||||||
if tabWidget:
|
if tabWidget:
|
||||||
self.console_widget.saveFileButton.setEnabled(tabWidget.isModified())
|
self.console_widget.save_file_action.setEnabled(tabWidget.isModified())
|
||||||
|
|
||||||
def enableToolBarEditor(self, enable):
|
def enableToolBarEditor(self, enable):
|
||||||
if self.topFrame.isVisible():
|
if self.topFrame.isVisible():
|
||||||
@ -866,7 +885,7 @@ class EditorTabWidget(QTabWidget):
|
|||||||
index = self.indexOf(tab)
|
index = self.indexOf(tab)
|
||||||
s = self.tabText(index)
|
s = self.tabText(index)
|
||||||
self.setTabTitle(index, f"*{s}" if modified else re.sub(r"^(\*)", "", s))
|
self.setTabTitle(index, f"*{s}" if modified else re.sub(r"^(\*)", "", s))
|
||||||
self.console_widget.saveFileButton.setEnabled(modified)
|
self.console_widget.save_file_action.setEnabled(modified)
|
||||||
|
|
||||||
def setTabTitle(self, tab, title):
|
def setTabTitle(self, tab, title):
|
||||||
self.setTabText(tab, title)
|
self.setTabText(tab, title)
|
||||||
@ -1073,10 +1092,9 @@ class EditorTabWidget(QTabWidget):
|
|||||||
objInspectorEnabled = QgsSettings().value(
|
objInspectorEnabled = QgsSettings().value(
|
||||||
"pythonConsole/enableObjectInsp", False, type=bool
|
"pythonConsole/enableObjectInsp", False, type=bool
|
||||||
)
|
)
|
||||||
listObj = self.console_widget.objectListButton
|
|
||||||
if self.console_widget.listClassMethod.isVisible():
|
if self.console_widget.listClassMethod.isVisible():
|
||||||
listObj.setChecked(objInspectorEnabled)
|
self.console_widget.object_inspector_action.setChecked(objInspectorEnabled)
|
||||||
listObj.setEnabled(objInspectorEnabled)
|
self.console_widget.object_inspector_action.setEnabled(objInspectorEnabled)
|
||||||
if objInspectorEnabled:
|
if objInspectorEnabled:
|
||||||
cW = self.currentWidget()
|
cW = self.currentWidget()
|
||||||
if cW and not self.console_widget.listClassMethod.isVisible():
|
if cW and not self.console_widget.listClassMethod.isVisible():
|
||||||
|
@ -348,7 +348,7 @@ class ShellOutputScintilla(QgsCodeEditorPython):
|
|||||||
Ed = self.console_widget.splitterObj
|
Ed = self.console_widget.splitterObj
|
||||||
if not Ed.isVisible():
|
if not Ed.isVisible():
|
||||||
Ed.show()
|
Ed.show()
|
||||||
self.console_widget.showEditorButton.setChecked(True)
|
self.console_widget.show_editor_action.setChecked(True)
|
||||||
self.shell_editor.setFocus()
|
self.shell_editor.setFocus()
|
||||||
|
|
||||||
def copy(self):
|
def copy(self):
|
||||||
|
@ -11645,6 +11645,25 @@ Qgis.RasterProcessingParameterCapability.__doc__ = """Capabilities of a raster l
|
|||||||
Qgis.RasterProcessingParameterCapability.baseClass = Qgis
|
Qgis.RasterProcessingParameterCapability.baseClass = Qgis
|
||||||
Qgis.RasterProcessingParameterCapabilities.baseClass = Qgis
|
Qgis.RasterProcessingParameterCapabilities.baseClass = Qgis
|
||||||
RasterProcessingParameterCapabilities = Qgis # dirty hack since SIP seems to introduce the flags in module
|
RasterProcessingParameterCapabilities = Qgis # dirty hack since SIP seems to introduce the flags in module
|
||||||
|
# monkey patching scoped based enum
|
||||||
|
Qgis.DevToolsNodeRole.Status.__doc__ = "Request status role"
|
||||||
|
Qgis.DevToolsNodeRole.Id.__doc__ = "Request ID role"
|
||||||
|
Qgis.DevToolsNodeRole.ElapsedTime.__doc__ = "Elapsed time"
|
||||||
|
Qgis.DevToolsNodeRole.MaximumTime.__doc__ = "Maximum encountered elapsed time"
|
||||||
|
Qgis.DevToolsNodeRole.Sort.__doc__ = "Sort order role"
|
||||||
|
Qgis.DevToolsNodeRole.__doc__ = """Dev tools node custom data roles.
|
||||||
|
|
||||||
|
.. versionadded:: 4.0
|
||||||
|
|
||||||
|
* ``Status``: Request status role
|
||||||
|
* ``Id``: Request ID role
|
||||||
|
* ``ElapsedTime``: Elapsed time
|
||||||
|
* ``MaximumTime``: Maximum encountered elapsed time
|
||||||
|
* ``Sort``: Sort order role
|
||||||
|
|
||||||
|
"""
|
||||||
|
# --
|
||||||
|
Qgis.DevToolsNodeRole.baseClass = Qgis
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
|
|
||||||
|
|
||||||
|
@ -3446,6 +3446,15 @@ The development version
|
|||||||
typedef QFlags<Qgis::RasterProcessingParameterCapability> RasterProcessingParameterCapabilities;
|
typedef QFlags<Qgis::RasterProcessingParameterCapability> RasterProcessingParameterCapabilities;
|
||||||
|
|
||||||
|
|
||||||
|
enum class DevToolsNodeRole
|
||||||
|
{
|
||||||
|
Status,
|
||||||
|
Id,
|
||||||
|
ElapsedTime,
|
||||||
|
MaximumTime,
|
||||||
|
Sort,
|
||||||
|
};
|
||||||
|
|
||||||
static const double DEFAULT_SEARCH_RADIUS_MM;
|
static const double DEFAULT_SEARCH_RADIUS_MM;
|
||||||
|
|
||||||
static const float DEFAULT_MAPTOPIXEL_THRESHOLD;
|
static const float DEFAULT_MAPTOPIXEL_THRESHOLD;
|
||||||
|
@ -73,18 +73,15 @@ Returns the format name for cloud optimized formats
|
|||||||
.. versionadded:: 3.42
|
.. versionadded:: 3.42
|
||||||
%End
|
%End
|
||||||
|
|
||||||
QgsMimeDataUtils::Uri uri() const;
|
QgsMimeDataUtils::Uri uri( const QString &authcfg = QString() ) const;
|
||||||
%Docstring
|
%Docstring
|
||||||
Returns a uri for the asset if it is a cloud optimized file like COG or
|
Returns a uri for the asset if it is a cloud optimized file like COG or
|
||||||
COPC, empty auth configuration
|
COPC.
|
||||||
|
|
||||||
.. versionadded:: 3.42
|
:param authcfg: Optional authentication configuration ID.
|
||||||
%End
|
|
||||||
|
|
||||||
QgsMimeDataUtils::Uri uri( const QString &authcfg ) const;
|
If the optional @authcfg parameter is set the authentication
|
||||||
%Docstring
|
configuration ID will be encoded in the returned URI.
|
||||||
Returns a uri for the asset if it is a cloud optimized file like COG or
|
|
||||||
COPC
|
|
||||||
|
|
||||||
.. versionadded:: 4.0
|
.. versionadded:: 4.0
|
||||||
%End
|
%End
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
Qgis.defaultProjectScales: src/core/qgis.h#L6171
|
Qgis.defaultProjectScales: src/core/qgis.h#L6185
|
||||||
Qgis.devVersion: src/core/qgis.h#L90
|
Qgis.devVersion: src/core/qgis.h#L90
|
||||||
Qgis.geoNone: src/core/qgis.h#L6223
|
Qgis.geoNone: src/core/qgis.h#L6237
|
||||||
Qgis.geoProj4: src/core/qgis.h#L6253
|
Qgis.geoProj4: src/core/qgis.h#L6267
|
||||||
Qgis.geoWkt: src/core/qgis.h#L6244
|
Qgis.geoWkt: src/core/qgis.h#L6258
|
||||||
Qgis.geographicCrsAuthId: src/core/qgis.h#L6233
|
Qgis.geographicCrsAuthId: src/core/qgis.h#L6247
|
||||||
Qgis.geosVersion: src/core/qgis.h#L6206
|
Qgis.geosVersion: src/core/qgis.h#L6220
|
||||||
Qgis.geosVersionInt: src/core/qgis.h#L6178
|
Qgis.geosVersionInt: src/core/qgis.h#L6192
|
||||||
Qgis.geosVersionMajor: src/core/qgis.h#L6185
|
Qgis.geosVersionMajor: src/core/qgis.h#L6199
|
||||||
Qgis.geosVersionMinor: src/core/qgis.h#L6192
|
Qgis.geosVersionMinor: src/core/qgis.h#L6206
|
||||||
Qgis.geosVersionPatch: src/core/qgis.h#L6199
|
Qgis.geosVersionPatch: src/core/qgis.h#L6213
|
||||||
Qgis.hasQtWebkit: src/core/qgis.h#L6213
|
Qgis.hasQtWebkit: src/core/qgis.h#L6227
|
||||||
Qgis.releaseName: src/core/qgis.h#L80
|
Qgis.releaseName: src/core/qgis.h#L80
|
||||||
Qgis.version: src/core/qgis.h#L66
|
Qgis.version: src/core/qgis.h#L66
|
||||||
Qgis.versionInt: src/core/qgis.h#L73
|
Qgis.versionInt: src/core/qgis.h#L73
|
||||||
|
18
python/gui/auto_additions/qgsshortcutsmanager.py
Normal file
18
python/gui/auto_additions/qgsshortcutsmanager.py
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
# The following has been generated automatically from src/gui/qgsshortcutsmanager.h
|
||||||
|
# monkey patching scoped based enum
|
||||||
|
QgsShortcutsManager.CommonAction.CodeToggleComment.__doc__ = "Toggle code comments"
|
||||||
|
QgsShortcutsManager.CommonAction.CodeReformat.__doc__ = "Reformat code"
|
||||||
|
QgsShortcutsManager.CommonAction.CodeRunScript.__doc__ = "Run script"
|
||||||
|
QgsShortcutsManager.CommonAction.CodeRunSelection.__doc__ = "Run selection from script"
|
||||||
|
QgsShortcutsManager.CommonAction.__doc__ = """Contains common actions which are used across a variety of classes.
|
||||||
|
|
||||||
|
.. versionadded:: 4.0
|
||||||
|
|
||||||
|
* ``CodeToggleComment``: Toggle code comments
|
||||||
|
* ``CodeReformat``: Reformat code
|
||||||
|
* ``CodeRunScript``: Run script
|
||||||
|
* ``CodeRunSelection``: Run selection from script
|
||||||
|
|
||||||
|
"""
|
||||||
|
# --
|
||||||
|
QgsShortcutsManager.CommonAction.baseClass = QgsShortcutsManager
|
@ -23,6 +23,14 @@ rather accessed through :py:func:`QgsGui.shortcutsManager()`.
|
|||||||
#include "qgsshortcutsmanager.h"
|
#include "qgsshortcutsmanager.h"
|
||||||
%End
|
%End
|
||||||
public:
|
public:
|
||||||
|
enum class CommonAction
|
||||||
|
{
|
||||||
|
CodeToggleComment,
|
||||||
|
CodeReformat,
|
||||||
|
CodeRunScript,
|
||||||
|
CodeRunSelection,
|
||||||
|
};
|
||||||
|
|
||||||
QgsShortcutsManager( QObject *parent /TransferThis/ = 0, const QString &settingsRoot = "/shortcuts/" );
|
QgsShortcutsManager( QObject *parent /TransferThis/ = 0, const QString &settingsRoot = "/shortcuts/" );
|
||||||
%Docstring
|
%Docstring
|
||||||
Constructor for QgsShortcutsManager.
|
Constructor for QgsShortcutsManager.
|
||||||
@ -36,6 +44,8 @@ Constructor for QgsShortcutsManager.
|
|||||||
QGIS actions.
|
QGIS actions.
|
||||||
%End
|
%End
|
||||||
|
|
||||||
|
~QgsShortcutsManager();
|
||||||
|
|
||||||
void registerAllChildren( QObject *object, bool recursive = false, const QString §ion = QString() );
|
void registerAllChildren( QObject *object, bool recursive = false, const QString §ion = QString() );
|
||||||
%Docstring
|
%Docstring
|
||||||
Automatically registers all QActions and QShortcuts which are children
|
Automatically registers all QActions and QShortcuts which are children
|
||||||
@ -105,6 +115,16 @@ in GUI.
|
|||||||
.. seealso:: :py:func:`unregisterAction`
|
.. seealso:: :py:func:`unregisterAction`
|
||||||
|
|
||||||
.. seealso:: :py:func:`registerAllChildActions`
|
.. seealso:: :py:func:`registerAllChildActions`
|
||||||
|
%End
|
||||||
|
|
||||||
|
void initializeCommonAction( QAction *action, CommonAction commonAction );
|
||||||
|
%Docstring
|
||||||
|
Initializes an ``action`` as a common action.
|
||||||
|
|
||||||
|
This automatically configures the ``action`` to use the properties for
|
||||||
|
the common action, such as setting the action's tooltip and shortcut.
|
||||||
|
|
||||||
|
.. versionadded:: 4.0
|
||||||
%End
|
%End
|
||||||
|
|
||||||
bool registerShortcut( QShortcut *shortcut, const QString &defaultSequence = QString(), const QString §ion = QString() );
|
bool registerShortcut( QShortcut *shortcut, const QString &defaultSequence = QString(), const QString §ion = QString() );
|
||||||
@ -290,6 +310,14 @@ if no shortcut is associated.
|
|||||||
.. seealso:: :py:func:`objectForSequence`
|
.. seealso:: :py:func:`objectForSequence`
|
||||||
|
|
||||||
.. seealso:: :py:func:`actionForSequence`
|
.. seealso:: :py:func:`actionForSequence`
|
||||||
|
%End
|
||||||
|
|
||||||
|
QKeySequence sequenceForCommonAction( CommonAction action ) const;
|
||||||
|
%Docstring
|
||||||
|
Returns the key sequence which is associated with a common ``action``,
|
||||||
|
or an empty sequence if no shortcut is assigned to that action.
|
||||||
|
|
||||||
|
.. versionadded:: 4.0
|
||||||
%End
|
%End
|
||||||
|
|
||||||
QAction *actionByName( const QString &name ) const;
|
QAction *actionByName( const QString &name ) const;
|
||||||
|
@ -6977,28 +6977,30 @@ QgsShapeburstFillSymbolLayerWidget.setColor: src/gui/symbology/qgssymbollayerwid
|
|||||||
QgsShapeburstFillSymbolLayerWidget.setSymbolLayer: src/gui/symbology/qgssymbollayerwidget.h#L426
|
QgsShapeburstFillSymbolLayerWidget.setSymbolLayer: src/gui/symbology/qgssymbollayerwidget.h#L426
|
||||||
QgsShapeburstFillSymbolLayerWidget.symbolLayer: src/gui/symbology/qgssymbollayerwidget.h#L427
|
QgsShapeburstFillSymbolLayerWidget.symbolLayer: src/gui/symbology/qgssymbollayerwidget.h#L427
|
||||||
QgsShapeburstFillSymbolLayerWidget: src/gui/symbology/qgssymbollayerwidget.h#L407
|
QgsShapeburstFillSymbolLayerWidget: src/gui/symbology/qgssymbollayerwidget.h#L407
|
||||||
QgsShortcutsManager.actionByName: src/gui/qgsshortcutsmanager.h#L236
|
QgsShortcutsManager.actionByName: src/gui/qgsshortcutsmanager.h#L267
|
||||||
QgsShortcutsManager.actionForSequence: src/gui/qgsshortcutsmanager.h#L221
|
QgsShortcutsManager.actionForSequence: src/gui/qgsshortcutsmanager.h#L246
|
||||||
QgsShortcutsManager.defaultKeySequence: src/gui/qgsshortcutsmanager.h#L163
|
QgsShortcutsManager.defaultKeySequence: src/gui/qgsshortcutsmanager.h#L188
|
||||||
QgsShortcutsManager.defaultKeySequence: src/gui/qgsshortcutsmanager.h#L171
|
QgsShortcutsManager.defaultKeySequence: src/gui/qgsshortcutsmanager.h#L196
|
||||||
QgsShortcutsManager.objectDefaultKeySequence: src/gui/qgsshortcutsmanager.h#L155
|
QgsShortcutsManager.initializeCommonAction: src/gui/qgsshortcutsmanager.h#L119
|
||||||
QgsShortcutsManager.objectForSequence: src/gui/qgsshortcutsmanager.h#L213
|
QgsShortcutsManager.objectDefaultKeySequence: src/gui/qgsshortcutsmanager.h#L180
|
||||||
QgsShortcutsManager.objectForSettingKey: src/gui/qgsshortcutsmanager.h#L262
|
QgsShortcutsManager.objectForSequence: src/gui/qgsshortcutsmanager.h#L238
|
||||||
QgsShortcutsManager.objectSettingKey: src/gui/qgsshortcutsmanager.h#L254
|
QgsShortcutsManager.objectForSettingKey: src/gui/qgsshortcutsmanager.h#L293
|
||||||
QgsShortcutsManager.registerAction: src/gui/qgsshortcutsmanager.h#L94
|
QgsShortcutsManager.objectSettingKey: src/gui/qgsshortcutsmanager.h#L285
|
||||||
QgsShortcutsManager.registerAllChildActions: src/gui/qgsshortcutsmanager.h#L70
|
QgsShortcutsManager.registerAction: src/gui/qgsshortcutsmanager.h#L109
|
||||||
QgsShortcutsManager.registerAllChildShortcuts: src/gui/qgsshortcutsmanager.h#L81
|
QgsShortcutsManager.registerAllChildActions: src/gui/qgsshortcutsmanager.h#L85
|
||||||
QgsShortcutsManager.registerAllChildren: src/gui/qgsshortcutsmanager.h#L59
|
QgsShortcutsManager.registerAllChildShortcuts: src/gui/qgsshortcutsmanager.h#L96
|
||||||
QgsShortcutsManager.registerShortcut: src/gui/qgsshortcutsmanager.h#L106
|
QgsShortcutsManager.registerAllChildren: src/gui/qgsshortcutsmanager.h#L74
|
||||||
QgsShortcutsManager.setKeySequence: src/gui/qgsshortcutsmanager.h#L180
|
QgsShortcutsManager.registerShortcut: src/gui/qgsshortcutsmanager.h#L131
|
||||||
QgsShortcutsManager.setKeySequence: src/gui/qgsshortcutsmanager.h#L196
|
QgsShortcutsManager.sequenceForCommonAction: src/gui/qgsshortcutsmanager.h#L260
|
||||||
QgsShortcutsManager.setKeySequence: src/gui/qgsshortcutsmanager.h#L204
|
QgsShortcutsManager.setKeySequence: src/gui/qgsshortcutsmanager.h#L205
|
||||||
QgsShortcutsManager.setObjectKeySequence: src/gui/qgsshortcutsmanager.h#L188
|
QgsShortcutsManager.setKeySequence: src/gui/qgsshortcutsmanager.h#L221
|
||||||
QgsShortcutsManager.settingsPath: src/gui/qgsshortcutsmanager.h#L246
|
QgsShortcutsManager.setKeySequence: src/gui/qgsshortcutsmanager.h#L229
|
||||||
QgsShortcutsManager.shortcutByName: src/gui/qgsshortcutsmanager.h#L243
|
QgsShortcutsManager.setObjectKeySequence: src/gui/qgsshortcutsmanager.h#L213
|
||||||
QgsShortcutsManager.shortcutForSequence: src/gui/qgsshortcutsmanager.h#L229
|
QgsShortcutsManager.settingsPath: src/gui/qgsshortcutsmanager.h#L277
|
||||||
QgsShortcutsManager.unregisterAction: src/gui/qgsshortcutsmanager.h#L116
|
QgsShortcutsManager.shortcutByName: src/gui/qgsshortcutsmanager.h#L274
|
||||||
QgsShortcutsManager.unregisterShortcut: src/gui/qgsshortcutsmanager.h#L126
|
QgsShortcutsManager.shortcutForSequence: src/gui/qgsshortcutsmanager.h#L254
|
||||||
|
QgsShortcutsManager.unregisterAction: src/gui/qgsshortcutsmanager.h#L141
|
||||||
|
QgsShortcutsManager.unregisterShortcut: src/gui/qgsshortcutsmanager.h#L151
|
||||||
QgsShortcutsManager: src/gui/qgsshortcutsmanager.h#L36
|
QgsShortcutsManager: src/gui/qgsshortcutsmanager.h#L36
|
||||||
QgsSimpleFillSymbolLayerWidget.create: src/gui/symbology/qgssymbollayerwidget.h#L264
|
QgsSimpleFillSymbolLayerWidget.create: src/gui/symbology/qgssymbollayerwidget.h#L264
|
||||||
QgsSimpleFillSymbolLayerWidget.setColor: src/gui/symbology/qgssymbollayerwidget.h#L271
|
QgsSimpleFillSymbolLayerWidget.setColor: src/gui/symbology/qgssymbollayerwidget.h#L271
|
||||||
|
@ -99,7 +99,13 @@ class SpatiaLiteDBPlugin(DBPlugin):
|
|||||||
QApplication.restoreOverrideCursor()
|
QApplication.restoreOverrideCursor()
|
||||||
try:
|
try:
|
||||||
filename, selected_filter = QFileDialog.getOpenFileName(
|
filename, selected_filter = QFileDialog.getOpenFileName(
|
||||||
parent, "Choose SQLite/SpatiaLite file"
|
parent,
|
||||||
|
parent.tr("Choose SQLite/SpatiaLite file"),
|
||||||
|
None,
|
||||||
|
parent.tr("SpatiaLite DB")
|
||||||
|
+ " (*.sqlite *.db *.sqlite3 *.db3 *.s3db);;"
|
||||||
|
+ parent.tr("All files")
|
||||||
|
+ " (*)",
|
||||||
)
|
)
|
||||||
if not filename:
|
if not filename:
|
||||||
return
|
return
|
||||||
|
@ -266,6 +266,19 @@ class merge(GdalAlgorithm):
|
|||||||
arguments.append(list_file)
|
arguments.append(list_file)
|
||||||
|
|
||||||
return [
|
return [
|
||||||
self.commandName() + (".bat" if isWindows() else ".py"),
|
self.commandName() + merge.command_ext(),
|
||||||
GdalUtils.escapeAndJoin(arguments),
|
GdalUtils.escapeAndJoin(arguments),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def command_ext() -> str:
|
||||||
|
"""
|
||||||
|
Returns the gdal_merge command extension
|
||||||
|
"""
|
||||||
|
if isWindows():
|
||||||
|
return ".bat"
|
||||||
|
|
||||||
|
if GdalUtils.version() < 3090000:
|
||||||
|
return ".py"
|
||||||
|
|
||||||
|
return ""
|
||||||
|
@ -30,7 +30,7 @@ from qgis.PyQt.QtCore import Qt
|
|||||||
from qgis.PyQt.QtGui import QPalette
|
from qgis.PyQt.QtGui import QPalette
|
||||||
from qgis.PyQt.QtWidgets import QMessageBox, QFileDialog, QVBoxLayout
|
from qgis.PyQt.QtWidgets import QMessageBox, QFileDialog, QVBoxLayout
|
||||||
|
|
||||||
from qgis.gui import QgsGui, QgsErrorDialog, QgsCodeEditorWidget
|
from qgis.gui import QgsGui, QgsErrorDialog, QgsCodeEditorWidget, QgsShortcutsManager
|
||||||
from qgis.core import (
|
from qgis.core import (
|
||||||
QgsApplication,
|
QgsApplication,
|
||||||
QgsFileUtils,
|
QgsFileUtils,
|
||||||
@ -122,6 +122,9 @@ class ScriptEditorDialog(BASE, WIDGET):
|
|||||||
self.palette().color(QPalette.ColorRole.WindowText),
|
self.palette().color(QPalette.ColorRole.WindowText),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
QgsGui.shortcutsManager().initializeCommonAction(
|
||||||
|
self.actionToggleComment, QgsShortcutsManager.CommonAction.CodeToggleComment
|
||||||
|
)
|
||||||
|
|
||||||
# Connect signals and slots
|
# Connect signals and slots
|
||||||
self.actionOpenScript.triggered.connect(self.openScript)
|
self.actionOpenScript.triggered.connect(self.openScript)
|
||||||
|
@ -4866,6 +4866,8 @@ class TestGdalRasterAlgorithms(QgisTestCase, AlgorithmsTestBase.AlgorithmsTest):
|
|||||||
alg = merge()
|
alg = merge()
|
||||||
alg.initAlgorithm()
|
alg.initAlgorithm()
|
||||||
|
|
||||||
|
merge_command = alg.commandName() + alg.command_ext()
|
||||||
|
|
||||||
with tempfile.TemporaryDirectory() as outdir:
|
with tempfile.TemporaryDirectory() as outdir:
|
||||||
# this algorithm creates temporary text file with input layers
|
# this algorithm creates temporary text file with input layers
|
||||||
# so we strip its path, leaving only filename
|
# so we strip its path, leaving only filename
|
||||||
@ -4877,7 +4879,7 @@ class TestGdalRasterAlgorithms(QgisTestCase, AlgorithmsTestBase.AlgorithmsTest):
|
|||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
cmd,
|
cmd,
|
||||||
[
|
[
|
||||||
"gdal_merge.py",
|
merge_command,
|
||||||
"-ot Float32 -of GTiff "
|
"-ot Float32 -of GTiff "
|
||||||
+ "-o "
|
+ "-o "
|
||||||
+ outdir
|
+ outdir
|
||||||
@ -4896,7 +4898,7 @@ class TestGdalRasterAlgorithms(QgisTestCase, AlgorithmsTestBase.AlgorithmsTest):
|
|||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
cmd,
|
cmd,
|
||||||
[
|
[
|
||||||
"gdal_merge.py",
|
merge_command,
|
||||||
"-separate -ot Float32 -of GTiff "
|
"-separate -ot Float32 -of GTiff "
|
||||||
+ "-o "
|
+ "-o "
|
||||||
+ outdir
|
+ outdir
|
||||||
@ -4920,7 +4922,7 @@ class TestGdalRasterAlgorithms(QgisTestCase, AlgorithmsTestBase.AlgorithmsTest):
|
|||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
cmd,
|
cmd,
|
||||||
[
|
[
|
||||||
"gdal_merge.py",
|
merge_command,
|
||||||
"-ot Float32 -of GTiff -tap -ps 0.1 0.1 "
|
"-ot Float32 -of GTiff -tap -ps 0.1 0.1 "
|
||||||
+ "-o "
|
+ "-o "
|
||||||
+ outdir
|
+ outdir
|
||||||
@ -4944,7 +4946,7 @@ class TestGdalRasterAlgorithms(QgisTestCase, AlgorithmsTestBase.AlgorithmsTest):
|
|||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
cmd,
|
cmd,
|
||||||
[
|
[
|
||||||
"gdal_merge.py",
|
merge_command,
|
||||||
"-a_nodata -9999.0 -ot Float32 -of GTiff "
|
"-a_nodata -9999.0 -ot Float32 -of GTiff "
|
||||||
+ "-o "
|
+ "-o "
|
||||||
+ outdir
|
+ outdir
|
||||||
|
@ -41,7 +41,7 @@ def userFolder():
|
|||||||
|
|
||||||
|
|
||||||
def defaultOutputFolder():
|
def defaultOutputFolder():
|
||||||
folder = os.path.join(userFolder(), "outputs")
|
folder = os.path.join(QDir.homePath(), "processing")
|
||||||
if not QDir(folder).exists():
|
if not QDir(folder).exists():
|
||||||
QDir().mkpath(folder)
|
QDir().mkpath(folder)
|
||||||
|
|
||||||
|
@ -988,6 +988,11 @@ QTreeView::item:selected, QTreeView::branch:selected {
|
|||||||
color: @text;
|
color: @text;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QTreeView::item:selected:disabled, QTreeView::branch:selected:disabled {
|
||||||
|
background-color: @itemalternativebackground;
|
||||||
|
color: @background;
|
||||||
|
}
|
||||||
|
|
||||||
QTreeView::branch:has-children:!has-siblings:closed,
|
QTreeView::branch:has-children:!has-siblings:closed,
|
||||||
QTreeView::branch:closed:has-children:has-siblings {
|
QTreeView::branch:closed:has-children:has-siblings {
|
||||||
border-image: none;
|
border-image: none;
|
||||||
|
@ -1019,6 +1019,11 @@ QTreeView::item:selected, QTreeView::branch:selected {
|
|||||||
color: @textlight;
|
color: @textlight;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QTreeView::item:selected:disabled, QTreeView::branch:selected:disabled {
|
||||||
|
background-color: @itemdarkbackground;
|
||||||
|
color: @background;
|
||||||
|
}
|
||||||
|
|
||||||
QTreeView::branch:has-children:!has-siblings:closed,
|
QTreeView::branch:has-children:!has-siblings:closed,
|
||||||
QTreeView::branch:closed:has-children:has-siblings {
|
QTreeView::branch:closed:has-children:has-siblings {
|
||||||
border-image: none;
|
border-image: none;
|
||||||
|
@ -8,6 +8,8 @@ set(QGIS_3D_SRCS
|
|||||||
qgsaabb.cpp
|
qgsaabb.cpp
|
||||||
qgsabstract3dengine.cpp
|
qgsabstract3dengine.cpp
|
||||||
qgsabstractvectorlayer3drenderer.cpp
|
qgsabstractvectorlayer3drenderer.cpp
|
||||||
|
qgsannotationlayer3drenderer.cpp
|
||||||
|
qgsannotationlayerchunkloader_p.cpp
|
||||||
qgs3danimationsettings.cpp
|
qgs3danimationsettings.cpp
|
||||||
qgs3dexportobject.cpp
|
qgs3dexportobject.cpp
|
||||||
qgs3dmapexportsettings.cpp
|
qgs3dmapexportsettings.cpp
|
||||||
@ -45,6 +47,7 @@ set(QGIS_3D_SRCS
|
|||||||
qgscolorramptexture.cpp
|
qgscolorramptexture.cpp
|
||||||
qgsrubberband3d.cpp
|
qgsrubberband3d.cpp
|
||||||
qgsambientocclusionsettings.cpp
|
qgsambientocclusionsettings.cpp
|
||||||
|
qgstextureatlasgenerator.cpp
|
||||||
|
|
||||||
qgspointcloudlayer3drenderer.cpp
|
qgspointcloudlayer3drenderer.cpp
|
||||||
qgspointcloudlayerchunkloader_p.cpp
|
qgspointcloudlayerchunkloader_p.cpp
|
||||||
@ -153,6 +156,8 @@ set(QGIS_3D_HDRS
|
|||||||
qgsaabb.h
|
qgsaabb.h
|
||||||
qgsabstract3dengine.h
|
qgsabstract3dengine.h
|
||||||
qgsabstractvectorlayer3drenderer.h
|
qgsabstractvectorlayer3drenderer.h
|
||||||
|
qgsannotationlayer3drenderer.h
|
||||||
|
qgsannotationlayerchunkloader_p.h
|
||||||
qgscameracontroller.h
|
qgscameracontroller.h
|
||||||
qgscamerapose.h
|
qgscamerapose.h
|
||||||
qgsgeotransform.h
|
qgsgeotransform.h
|
||||||
@ -174,6 +179,7 @@ set(QGIS_3D_HDRS
|
|||||||
qgsshadowsettings.h
|
qgsshadowsettings.h
|
||||||
qgspointcloudlayer3drenderer.h
|
qgspointcloudlayer3drenderer.h
|
||||||
qgsambientocclusionsettings.h
|
qgsambientocclusionsettings.h
|
||||||
|
qgstextureatlasgenerator.h
|
||||||
|
|
||||||
lights/qgsdirectionallightsettings.h
|
lights/qgsdirectionallightsettings.h
|
||||||
lights/qgslightsource.h
|
lights/qgslightsource.h
|
||||||
@ -312,6 +318,7 @@ target_include_directories(qgis_3d PUBLIC
|
|||||||
${CMAKE_BINARY_DIR}/src/3d
|
${CMAKE_BINARY_DIR}/src/3d
|
||||||
${CMAKE_SOURCE_DIR}/external/delaunator-cpp
|
${CMAKE_SOURCE_DIR}/external/delaunator-cpp
|
||||||
${CMAKE_SOURCE_DIR}/external/tinygltf
|
${CMAKE_SOURCE_DIR}/external/tinygltf
|
||||||
|
${CMAKE_SOURCE_DIR}/external/rectpack2D
|
||||||
)
|
)
|
||||||
|
|
||||||
target_link_libraries(qgis_3d
|
target_link_libraries(qgis_3d
|
||||||
|
@ -27,6 +27,7 @@
|
|||||||
#include "qgsmeshlayer3drenderer.h"
|
#include "qgsmeshlayer3drenderer.h"
|
||||||
#include "qgspointcloudlayer3drenderer.h"
|
#include "qgspointcloudlayer3drenderer.h"
|
||||||
#include "qgstiledscenelayer3drenderer.h"
|
#include "qgstiledscenelayer3drenderer.h"
|
||||||
|
#include "qgsannotationlayer3drenderer.h"
|
||||||
#include "qgs3dsymbolregistry.h"
|
#include "qgs3dsymbolregistry.h"
|
||||||
#include "qgspoint3dsymbol.h"
|
#include "qgspoint3dsymbol.h"
|
||||||
#include "qgsline3dsymbol.h"
|
#include "qgsline3dsymbol.h"
|
||||||
@ -68,6 +69,7 @@ void Qgs3D::initialize()
|
|||||||
QgsApplication::renderer3DRegistry()->addRenderer( new QgsMeshLayer3DRendererMetadata );
|
QgsApplication::renderer3DRegistry()->addRenderer( new QgsMeshLayer3DRendererMetadata );
|
||||||
QgsApplication::renderer3DRegistry()->addRenderer( new QgsPointCloudLayer3DRendererMetadata );
|
QgsApplication::renderer3DRegistry()->addRenderer( new QgsPointCloudLayer3DRendererMetadata );
|
||||||
QgsApplication::renderer3DRegistry()->addRenderer( new QgsTiledSceneLayer3DRendererMetadata );
|
QgsApplication::renderer3DRegistry()->addRenderer( new QgsTiledSceneLayer3DRendererMetadata );
|
||||||
|
QgsApplication::renderer3DRegistry()->addRenderer( new QgsAnnotationLayer3DRendererMetadata );
|
||||||
|
|
||||||
QgsApplication::symbol3DRegistry()->addSymbolType( new Qgs3DSymbolMetadata( QStringLiteral( "point" ), QObject::tr( "Point" ), &QgsPoint3DSymbol::create, nullptr, Qgs3DSymbolImpl::handlerForPoint3DSymbol ) );
|
QgsApplication::symbol3DRegistry()->addSymbolType( new Qgs3DSymbolMetadata( QStringLiteral( "point" ), QObject::tr( "Point" ), &QgsPoint3DSymbol::create, nullptr, Qgs3DSymbolImpl::handlerForPoint3DSymbol ) );
|
||||||
QgsApplication::symbol3DRegistry()->addSymbolType( new Qgs3DSymbolMetadata( QStringLiteral( "line" ), QObject::tr( "Line" ), &QgsLine3DSymbol::create, nullptr, Qgs3DSymbolImpl::handlerForLine3DSymbol ) );
|
QgsApplication::symbol3DRegistry()->addSymbolType( new Qgs3DSymbolMetadata( QStringLiteral( "line" ), QObject::tr( "Line" ), &QgsLine3DSymbol::create, nullptr, Qgs3DSymbolImpl::handlerForLine3DSymbol ) );
|
||||||
|
@ -45,6 +45,7 @@
|
|||||||
#include "qgsapplication.h"
|
#include "qgsapplication.h"
|
||||||
#include "qgsaabb.h"
|
#include "qgsaabb.h"
|
||||||
#include "qgsabstract3dengine.h"
|
#include "qgsabstract3dengine.h"
|
||||||
|
#include "qgsannotationlayer.h"
|
||||||
#include "qgs3dmapsettings.h"
|
#include "qgs3dmapsettings.h"
|
||||||
#include "qgs3dutils.h"
|
#include "qgs3dutils.h"
|
||||||
#include "qgsabstract3drenderer.h"
|
#include "qgsabstract3drenderer.h"
|
||||||
@ -66,6 +67,7 @@
|
|||||||
#include "qgsterraingenerator.h"
|
#include "qgsterraingenerator.h"
|
||||||
#include "qgstiledscenelayer.h"
|
#include "qgstiledscenelayer.h"
|
||||||
#include "qgstiledscenelayer3drenderer.h"
|
#include "qgstiledscenelayer3drenderer.h"
|
||||||
|
#include "qgsannotationlayer3drenderer.h"
|
||||||
#include "qgsdirectionallightsettings.h"
|
#include "qgsdirectionallightsettings.h"
|
||||||
#include "qgsvectorlayer.h"
|
#include "qgsvectorlayer.h"
|
||||||
#include "qgsvectorlayer3drenderer.h"
|
#include "qgsvectorlayer3drenderer.h"
|
||||||
@ -746,6 +748,11 @@ void Qgs3DMapScene::addLayerEntity( QgsMapLayer *layer )
|
|||||||
QgsTiledSceneLayer3DRenderer *tiledSceneRenderer = static_cast<QgsTiledSceneLayer3DRenderer *>( renderer );
|
QgsTiledSceneLayer3DRenderer *tiledSceneRenderer = static_cast<QgsTiledSceneLayer3DRenderer *>( renderer );
|
||||||
tiledSceneRenderer->setLayer( static_cast<QgsTiledSceneLayer *>( layer ) );
|
tiledSceneRenderer->setLayer( static_cast<QgsTiledSceneLayer *>( layer ) );
|
||||||
}
|
}
|
||||||
|
else if ( layer->type() == Qgis::LayerType::Annotation && renderer->type() == QLatin1String( "annotation" ) )
|
||||||
|
{
|
||||||
|
auto annotationLayerRenderer = qgis::down_cast<QgsAnnotationLayer3DRenderer *>( renderer );
|
||||||
|
annotationLayerRenderer->setLayer( qobject_cast<QgsAnnotationLayer *>( layer ) );
|
||||||
|
}
|
||||||
|
|
||||||
Qt3DCore::QEntity *newEntity = renderer->createEntity( &mMap );
|
Qt3DCore::QEntity *newEntity = renderer->createEntity( &mMap );
|
||||||
if ( newEntity )
|
if ( newEntity )
|
||||||
|
@ -83,6 +83,7 @@ typedef Qt3DCore::QGeometry Qt3DQGeometry;
|
|||||||
#include "qgs3dutils.h"
|
#include "qgs3dutils.h"
|
||||||
#include "qgsimagetexture.h"
|
#include "qgsimagetexture.h"
|
||||||
#include "qgstessellatedpolygongeometry.h"
|
#include "qgstessellatedpolygongeometry.h"
|
||||||
|
#include "qgsgeotransform.h"
|
||||||
|
|
||||||
#include <numeric>
|
#include <numeric>
|
||||||
|
|
||||||
@ -285,15 +286,15 @@ void Qgs3DSceneExporter::parseTerrain( QgsTerrainEntity *terrain, const QString
|
|||||||
switch ( generator->type() )
|
switch ( generator->type() )
|
||||||
{
|
{
|
||||||
case QgsTerrainGenerator::Dem:
|
case QgsTerrainGenerator::Dem:
|
||||||
terrainTile = getDemTerrainEntity( terrain, node );
|
terrainTile = getDemTerrainEntity( terrain, node, settings->origin() );
|
||||||
parseDemTile( terrainTile, layerName + QStringLiteral( "_" ) );
|
parseDemTile( terrainTile, layerName + QStringLiteral( "_" ) );
|
||||||
break;
|
break;
|
||||||
case QgsTerrainGenerator::Flat:
|
case QgsTerrainGenerator::Flat:
|
||||||
terrainTile = getFlatTerrainEntity( terrain, node );
|
terrainTile = getFlatTerrainEntity( terrain, node, settings->origin() );
|
||||||
parseFlatTile( terrainTile, layerName + QStringLiteral( "_" ) );
|
parseFlatTile( terrainTile, layerName + QStringLiteral( "_" ) );
|
||||||
break;
|
break;
|
||||||
case QgsTerrainGenerator::Mesh:
|
case QgsTerrainGenerator::Mesh:
|
||||||
terrainTile = getMeshTerrainEntity( terrain, node );
|
terrainTile = getMeshTerrainEntity( terrain, node, settings->origin() );
|
||||||
parseMeshTile( terrainTile, layerName + QStringLiteral( "_" ) );
|
parseMeshTile( terrainTile, layerName + QStringLiteral( "_" ) );
|
||||||
break;
|
break;
|
||||||
// TODO: implement other terrain types
|
// TODO: implement other terrain types
|
||||||
@ -304,7 +305,7 @@ void Qgs3DSceneExporter::parseTerrain( QgsTerrainEntity *terrain, const QString
|
|||||||
textureGenerator->setTextureSize( oldResolution );
|
textureGenerator->setTextureSize( oldResolution );
|
||||||
}
|
}
|
||||||
|
|
||||||
QgsTerrainTileEntity *Qgs3DSceneExporter::getFlatTerrainEntity( QgsTerrainEntity *terrain, QgsChunkNode *node )
|
QgsTerrainTileEntity *Qgs3DSceneExporter::getFlatTerrainEntity( QgsTerrainEntity *terrain, QgsChunkNode *node, const QgsVector3D &mapOrigin )
|
||||||
{
|
{
|
||||||
QgsFlatTerrainGenerator *generator = dynamic_cast<QgsFlatTerrainGenerator *>( terrain->mapSettings()->terrainGenerator() );
|
QgsFlatTerrainGenerator *generator = dynamic_cast<QgsFlatTerrainGenerator *>( terrain->mapSettings()->terrainGenerator() );
|
||||||
FlatTerrainChunkLoader *flatTerrainLoader = qobject_cast<FlatTerrainChunkLoader *>( generator->createChunkLoader( node ) );
|
FlatTerrainChunkLoader *flatTerrainLoader = qobject_cast<FlatTerrainChunkLoader *>( generator->createChunkLoader( node ) );
|
||||||
@ -314,10 +315,17 @@ QgsTerrainTileEntity *Qgs3DSceneExporter::getFlatTerrainEntity( QgsTerrainEntity
|
|||||||
// the entity we created will be deallocated once the scene exporter is deallocated
|
// the entity we created will be deallocated once the scene exporter is deallocated
|
||||||
Qt3DCore::QEntity *entity = flatTerrainLoader->createEntity( this );
|
Qt3DCore::QEntity *entity = flatTerrainLoader->createEntity( this );
|
||||||
QgsTerrainTileEntity *tileEntity = qobject_cast<QgsTerrainTileEntity *>( entity );
|
QgsTerrainTileEntity *tileEntity = qobject_cast<QgsTerrainTileEntity *>( entity );
|
||||||
|
|
||||||
|
const QList<QgsGeoTransform *> transforms = entity->findChildren<QgsGeoTransform *>();
|
||||||
|
for ( QgsGeoTransform *transform : transforms )
|
||||||
|
{
|
||||||
|
transform->setOrigin( mapOrigin );
|
||||||
|
}
|
||||||
|
|
||||||
return tileEntity;
|
return tileEntity;
|
||||||
}
|
}
|
||||||
|
|
||||||
QgsTerrainTileEntity *Qgs3DSceneExporter::getDemTerrainEntity( QgsTerrainEntity *terrain, QgsChunkNode *node )
|
QgsTerrainTileEntity *Qgs3DSceneExporter::getDemTerrainEntity( QgsTerrainEntity *terrain, QgsChunkNode *node, const QgsVector3D &mapOrigin )
|
||||||
{
|
{
|
||||||
// Just create a new tile (we don't need to export exact level of details as in the scene)
|
// Just create a new tile (we don't need to export exact level of details as in the scene)
|
||||||
// create the entity synchronously and then it will be deleted once our scene exporter instance is deallocated
|
// create the entity synchronously and then it will be deleted once our scene exporter instance is deallocated
|
||||||
@ -329,17 +337,31 @@ QgsTerrainTileEntity *Qgs3DSceneExporter::getDemTerrainEntity( QgsTerrainEntity
|
|||||||
if ( mExportTextures )
|
if ( mExportTextures )
|
||||||
terrain->textureGenerator()->waitForFinished();
|
terrain->textureGenerator()->waitForFinished();
|
||||||
QgsTerrainTileEntity *tileEntity = qobject_cast<QgsTerrainTileEntity *>( loader->createEntity( this ) );
|
QgsTerrainTileEntity *tileEntity = qobject_cast<QgsTerrainTileEntity *>( loader->createEntity( this ) );
|
||||||
|
|
||||||
|
const QList<QgsGeoTransform *> transforms = tileEntity->findChildren<QgsGeoTransform *>();
|
||||||
|
for ( QgsGeoTransform *transform : transforms )
|
||||||
|
{
|
||||||
|
transform->setOrigin( mapOrigin );
|
||||||
|
}
|
||||||
|
|
||||||
delete generator;
|
delete generator;
|
||||||
return tileEntity;
|
return tileEntity;
|
||||||
}
|
}
|
||||||
|
|
||||||
QgsTerrainTileEntity *Qgs3DSceneExporter::getMeshTerrainEntity( QgsTerrainEntity *terrain, QgsChunkNode *node )
|
QgsTerrainTileEntity *Qgs3DSceneExporter::getMeshTerrainEntity( QgsTerrainEntity *terrain, QgsChunkNode *node, const QgsVector3D &mapOrigin )
|
||||||
{
|
{
|
||||||
QgsMeshTerrainGenerator *generator = dynamic_cast<QgsMeshTerrainGenerator *>( terrain->mapSettings()->terrainGenerator() );
|
QgsMeshTerrainGenerator *generator = dynamic_cast<QgsMeshTerrainGenerator *>( terrain->mapSettings()->terrainGenerator() );
|
||||||
QgsMeshTerrainTileLoader *loader = qobject_cast<QgsMeshTerrainTileLoader *>( generator->createChunkLoader( node ) );
|
QgsMeshTerrainTileLoader *loader = qobject_cast<QgsMeshTerrainTileLoader *>( generator->createChunkLoader( node ) );
|
||||||
loader->start();
|
loader->start();
|
||||||
// TODO: export textures
|
// TODO: export textures
|
||||||
QgsTerrainTileEntity *tileEntity = qobject_cast<QgsTerrainTileEntity *>( loader->createEntity( this ) );
|
QgsTerrainTileEntity *tileEntity = qobject_cast<QgsTerrainTileEntity *>( loader->createEntity( this ) );
|
||||||
|
|
||||||
|
const QList<QgsGeoTransform *> transforms = tileEntity->findChildren<QgsGeoTransform *>();
|
||||||
|
for ( QgsGeoTransform *transform : transforms )
|
||||||
|
{
|
||||||
|
transform->setOrigin( mapOrigin );
|
||||||
|
}
|
||||||
|
|
||||||
return tileEntity;
|
return tileEntity;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,12 +37,13 @@ class QgsDemTerrainGenerator;
|
|||||||
class QgsChunkNode;
|
class QgsChunkNode;
|
||||||
class Qgs3DExportObject;
|
class Qgs3DExportObject;
|
||||||
class QgsTerrainTextureGenerator;
|
class QgsTerrainTextureGenerator;
|
||||||
|
class QgsVector3D;
|
||||||
class QgsVectorLayer;
|
class QgsVectorLayer;
|
||||||
class QgsPolygon3DSymbol;
|
class QgsPolygon3DSymbol;
|
||||||
class QgsLine3DSymbol;
|
class QgsLine3DSymbol;
|
||||||
class QgsPoint3DSymbol;
|
class QgsPoint3DSymbol;
|
||||||
class QgsMeshEntity;
|
class QgsMeshEntity;
|
||||||
class TestQgs3DRendering;
|
class TestQgs3DExporter;
|
||||||
|
|
||||||
#define SIP_NO_FILE
|
#define SIP_NO_FILE
|
||||||
|
|
||||||
@ -126,11 +127,11 @@ class _3D_EXPORT Qgs3DSceneExporter : public Qt3DCore::QEntity
|
|||||||
Qgs3DExportObject *processPoints( Qt3DCore::QEntity *entity, const QString &objectNamePrefix );
|
Qgs3DExportObject *processPoints( Qt3DCore::QEntity *entity, const QString &objectNamePrefix );
|
||||||
|
|
||||||
//! Returns a tile entity that contains the geometry to be exported and necessary scaling parameters
|
//! Returns a tile entity that contains the geometry to be exported and necessary scaling parameters
|
||||||
QgsTerrainTileEntity *getFlatTerrainEntity( QgsTerrainEntity *terrain, QgsChunkNode *node );
|
QgsTerrainTileEntity *getFlatTerrainEntity( QgsTerrainEntity *terrain, QgsChunkNode *node, const QgsVector3D &mapOrigin );
|
||||||
//! Returns a tile entity that contains the geometry to be exported and necessary scaling parameters
|
//! Returns a tile entity that contains the geometry to be exported and necessary scaling parameters
|
||||||
QgsTerrainTileEntity *getDemTerrainEntity( QgsTerrainEntity *terrain, QgsChunkNode *node );
|
QgsTerrainTileEntity *getDemTerrainEntity( QgsTerrainEntity *terrain, QgsChunkNode *node, const QgsVector3D &mapOrigin );
|
||||||
//! Returns a tile entity that contains the geometry to be exported and necessary scaling parameters
|
//! Returns a tile entity that contains the geometry to be exported and necessary scaling parameters
|
||||||
QgsTerrainTileEntity *getMeshTerrainEntity( QgsTerrainEntity *terrain, QgsChunkNode *node );
|
QgsTerrainTileEntity *getMeshTerrainEntity( QgsTerrainEntity *terrain, QgsChunkNode *node, const QgsVector3D &mapOrigin );
|
||||||
|
|
||||||
//! Constructs a Qgs3DExportObject from the DEM tile entity
|
//! Constructs a Qgs3DExportObject from the DEM tile entity
|
||||||
void parseDemTile( QgsTerrainTileEntity *tileEntity, const QString &layerName );
|
void parseDemTile( QgsTerrainTileEntity *tileEntity, const QString &layerName );
|
||||||
@ -157,7 +158,7 @@ class _3D_EXPORT Qgs3DSceneExporter : public Qt3DCore::QEntity
|
|||||||
friend QgsPolygon3DSymbol;
|
friend QgsPolygon3DSymbol;
|
||||||
friend QgsLine3DSymbol;
|
friend QgsLine3DSymbol;
|
||||||
friend QgsPoint3DSymbol;
|
friend QgsPoint3DSymbol;
|
||||||
friend TestQgs3DRendering;
|
friend TestQgs3DExporter;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // QGS3DSCENEEXPORTER_H
|
#endif // QGS3DSCENEEXPORTER_H
|
||||||
|
@ -1161,7 +1161,7 @@ QgsCameraPose Qgs3DUtils::lineSegmentToCameraPose( const QgsVector3D &startPoint
|
|||||||
|
|
||||||
std::unique_ptr<Qt3DRender::QCamera> Qgs3DUtils::copyCamera( Qt3DRender::QCamera *cam )
|
std::unique_ptr<Qt3DRender::QCamera> Qgs3DUtils::copyCamera( Qt3DRender::QCamera *cam )
|
||||||
{
|
{
|
||||||
std::unique_ptr<Qt3DRender::QCamera> copy = std::make_unique<Qt3DRender::QCamera>();
|
auto copy = std::make_unique<Qt3DRender::QCamera>();
|
||||||
copy->setPosition( cam->position() );
|
copy->setPosition( cam->position() );
|
||||||
copy->setViewCenter( cam->viewCenter() );
|
copy->setViewCenter( cam->viewCenter() );
|
||||||
copy->setUpVector( cam->upVector() );
|
copy->setUpVector( cam->upVector() );
|
||||||
|
155
src/3d/qgsannotationlayer3drenderer.cpp
Normal file
155
src/3d/qgsannotationlayer3drenderer.cpp
Normal file
@ -0,0 +1,155 @@
|
|||||||
|
/***************************************************************************
|
||||||
|
qgsannotationlayer3drenderer.cpp
|
||||||
|
--------------------------------------
|
||||||
|
Date : January 2020
|
||||||
|
Copyright : (C) 2020 by Martin Dobias
|
||||||
|
Email : wonder dot sk at gmail dot com
|
||||||
|
***************************************************************************
|
||||||
|
* *
|
||||||
|
* This program is free software; you can redistribute it and/or modify *
|
||||||
|
* it under the terms of the GNU General Public License as published by *
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or *
|
||||||
|
* (at your option) any later version. *
|
||||||
|
* *
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
#include "qgsannotationlayer3drenderer.h"
|
||||||
|
#include "qgsannotationlayer.h"
|
||||||
|
#include "qgsannotationlayerchunkloader_p.h"
|
||||||
|
#include "qgis.h"
|
||||||
|
|
||||||
|
//
|
||||||
|
// QgsAnnotationLayer3DRendererMetadata
|
||||||
|
//
|
||||||
|
|
||||||
|
QgsAnnotationLayer3DRendererMetadata::QgsAnnotationLayer3DRendererMetadata()
|
||||||
|
: Qgs3DRendererAbstractMetadata( QStringLiteral( "annotation" ) )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
QgsAbstract3DRenderer *QgsAnnotationLayer3DRendererMetadata::createRenderer( QDomElement &elem, const QgsReadWriteContext &context )
|
||||||
|
{
|
||||||
|
auto r = std::make_unique< QgsAnnotationLayer3DRenderer >();
|
||||||
|
r->readXml( elem, context );
|
||||||
|
return r.release();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// QgsAnnotationLayer3DRenderer
|
||||||
|
//
|
||||||
|
|
||||||
|
QgsAnnotationLayer3DRenderer::QgsAnnotationLayer3DRenderer() = default;
|
||||||
|
|
||||||
|
void QgsAnnotationLayer3DRenderer::setLayer( QgsAnnotationLayer *layer )
|
||||||
|
{
|
||||||
|
mLayerRef = QgsMapLayerRef( layer );
|
||||||
|
}
|
||||||
|
|
||||||
|
QgsAnnotationLayer *QgsAnnotationLayer3DRenderer::layer() const
|
||||||
|
{
|
||||||
|
return qobject_cast<QgsAnnotationLayer *>( mLayerRef.layer );
|
||||||
|
}
|
||||||
|
|
||||||
|
void QgsAnnotationLayer3DRenderer::resolveReferences( const QgsProject &project )
|
||||||
|
{
|
||||||
|
mLayerRef.resolve( &project );
|
||||||
|
}
|
||||||
|
|
||||||
|
bool QgsAnnotationLayer3DRenderer::showCalloutLines() const
|
||||||
|
{
|
||||||
|
return mShowCalloutLines;
|
||||||
|
}
|
||||||
|
|
||||||
|
void QgsAnnotationLayer3DRenderer::setShowCalloutLines( bool show )
|
||||||
|
{
|
||||||
|
mShowCalloutLines = show;
|
||||||
|
}
|
||||||
|
|
||||||
|
void QgsAnnotationLayer3DRenderer::setCalloutLineColor( const QColor &color )
|
||||||
|
{
|
||||||
|
mCalloutLineColor = color;
|
||||||
|
}
|
||||||
|
|
||||||
|
QColor QgsAnnotationLayer3DRenderer::calloutLineColor() const
|
||||||
|
{
|
||||||
|
return mCalloutLineColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
void QgsAnnotationLayer3DRenderer::setCalloutLineWidth( double width )
|
||||||
|
{
|
||||||
|
mCalloutLineWidth = width;
|
||||||
|
}
|
||||||
|
|
||||||
|
double QgsAnnotationLayer3DRenderer::calloutLineWidth() const
|
||||||
|
{
|
||||||
|
return mCalloutLineWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString QgsAnnotationLayer3DRenderer::type() const
|
||||||
|
{
|
||||||
|
return "annotation";
|
||||||
|
}
|
||||||
|
|
||||||
|
QgsAnnotationLayer3DRenderer *QgsAnnotationLayer3DRenderer::clone() const
|
||||||
|
{
|
||||||
|
auto r = std::make_unique< QgsAnnotationLayer3DRenderer >();
|
||||||
|
r->mLayerRef = mLayerRef;
|
||||||
|
r->mAltClamping = mAltClamping;
|
||||||
|
r->mZOffset = mZOffset;
|
||||||
|
r->mShowCalloutLines = mShowCalloutLines;
|
||||||
|
r->mCalloutLineColor = mCalloutLineColor;
|
||||||
|
r->mCalloutLineWidth = mCalloutLineWidth;
|
||||||
|
return r.release();
|
||||||
|
}
|
||||||
|
|
||||||
|
Qt3DCore::QEntity *QgsAnnotationLayer3DRenderer::createEntity( Qgs3DMapSettings *map ) const
|
||||||
|
{
|
||||||
|
QgsAnnotationLayer *l = layer();
|
||||||
|
|
||||||
|
if ( !l )
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
// For some cases we start with a maximal z range because we can't know this upfront, as it potentially involves terrain heights.
|
||||||
|
// This range will be refined after populating the nodes to the actual z range of the generated chunks nodes.
|
||||||
|
// Assuming the vertical height is in meter, then it's extremely unlikely that a real vertical
|
||||||
|
// height will exceed this amount!
|
||||||
|
constexpr double MINIMUM_ANNOTATION_Z_ESTIMATE = -100000;
|
||||||
|
constexpr double MAXIMUM_ANNOTATION_Z_ESTIMATE = 100000;
|
||||||
|
|
||||||
|
double minimumZ = MINIMUM_ANNOTATION_Z_ESTIMATE;
|
||||||
|
double maximumZ = MAXIMUM_ANNOTATION_Z_ESTIMATE;
|
||||||
|
switch ( mAltClamping )
|
||||||
|
{
|
||||||
|
case Qgis::AltitudeClamping::Absolute:
|
||||||
|
// special case where we DO know the exact z range upfront!
|
||||||
|
minimumZ = mZOffset;
|
||||||
|
maximumZ = mZOffset;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Qgis::AltitudeClamping::Relative:
|
||||||
|
case Qgis::AltitudeClamping::Terrain:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new QgsAnnotationLayerChunkedEntity( map, l, mAltClamping, mZOffset, mShowCalloutLines, mCalloutLineColor, mCalloutLineWidth, minimumZ, maximumZ );
|
||||||
|
}
|
||||||
|
|
||||||
|
void QgsAnnotationLayer3DRenderer::writeXml( QDomElement &elem, const QgsReadWriteContext & ) const
|
||||||
|
{
|
||||||
|
QDomDocument doc = elem.ownerDocument();
|
||||||
|
|
||||||
|
elem.setAttribute( QStringLiteral( "layer" ), mLayerRef.layerId );
|
||||||
|
elem.setAttribute( QStringLiteral( "clamping" ), qgsEnumValueToKey( mAltClamping ) );
|
||||||
|
elem.setAttribute( QStringLiteral( "offset" ), mZOffset );
|
||||||
|
if ( mShowCalloutLines )
|
||||||
|
elem.setAttribute( QStringLiteral( "callouts" ), QStringLiteral( "1" ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
void QgsAnnotationLayer3DRenderer::readXml( const QDomElement &elem, const QgsReadWriteContext & )
|
||||||
|
{
|
||||||
|
mLayerRef = QgsMapLayerRef( elem.attribute( QStringLiteral( "layer" ) ) );
|
||||||
|
mAltClamping = qgsEnumKeyToValue( elem.attribute( QStringLiteral( "clamping" ) ), Qgis::AltitudeClamping::Relative );
|
||||||
|
mZOffset = elem.attribute( QStringLiteral( "offset" ), QString::number( DEFAULT_Z_OFFSET ) ).toDouble();
|
||||||
|
mShowCalloutLines = elem.attribute( QStringLiteral( "callouts" ), QStringLiteral( "0" ) ).toInt();
|
||||||
|
}
|
185
src/3d/qgsannotationlayer3drenderer.h
Normal file
185
src/3d/qgsannotationlayer3drenderer.h
Normal file
@ -0,0 +1,185 @@
|
|||||||
|
/***************************************************************************
|
||||||
|
qgsannotationlayer3drenderer.h
|
||||||
|
--------------------------------------
|
||||||
|
Date : September 2025
|
||||||
|
Copyright : (C) 2025 by Nyall Dawson
|
||||||
|
Email : nyall dot dawson at gmail dot com
|
||||||
|
***************************************************************************
|
||||||
|
* *
|
||||||
|
* This program is free software; you can redistribute it and/or modify *
|
||||||
|
* it under the terms of the GNU General Public License as published by *
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or *
|
||||||
|
* (at your option) any later version. *
|
||||||
|
* *
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
#ifndef QGSANNOTATIONLAYER3DRENDERER_H
|
||||||
|
#define QGSANNOTATIONLAYER3DRENDERER_H
|
||||||
|
|
||||||
|
#include "qgis_3d.h"
|
||||||
|
#include "qgis_sip.h"
|
||||||
|
|
||||||
|
#include "qgs3drendererregistry.h"
|
||||||
|
#include "qgsabstract3drenderer.h"
|
||||||
|
#include "qgsmaplayerref.h"
|
||||||
|
|
||||||
|
class QgsAnnotationLayer;
|
||||||
|
|
||||||
|
#ifdef SIP_RUN
|
||||||
|
// this is needed for the "convert to subclass" code below to compile
|
||||||
|
% ModuleHeaderCode
|
||||||
|
#include "qgsannotationlayer3drenderer.h"
|
||||||
|
% End
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \ingroup core
|
||||||
|
* \brief Metadata for annotation layer 3D renderer to allow creation of its instances from XML.
|
||||||
|
*
|
||||||
|
* \warning This is not considered stable API, and may change in future QGIS releases. It is
|
||||||
|
* exposed to the Python bindings as a tech preview only.
|
||||||
|
*
|
||||||
|
* \since QGIS 4.0
|
||||||
|
*/
|
||||||
|
class _3D_EXPORT QgsAnnotationLayer3DRendererMetadata : public Qgs3DRendererAbstractMetadata
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
QgsAnnotationLayer3DRendererMetadata();
|
||||||
|
|
||||||
|
//! Creates an instance of a 3D renderer based on a DOM element with renderer configuration
|
||||||
|
QgsAbstract3DRenderer *createRenderer( QDomElement &elem, const QgsReadWriteContext &context ) override SIP_FACTORY;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \ingroup qgis_3d
|
||||||
|
* \brief 3D renderers for annotation layers.
|
||||||
|
*
|
||||||
|
* \since QGIS 4.0
|
||||||
|
*/
|
||||||
|
class _3D_EXPORT QgsAnnotationLayer3DRenderer : public QgsAbstract3DRenderer
|
||||||
|
{
|
||||||
|
#ifdef SIP_RUN
|
||||||
|
SIP_CONVERT_TO_SUBCLASS_CODE
|
||||||
|
if ( dynamic_cast<QgsAnnotationLayer3DRenderer *>( sipCpp ) != nullptr )
|
||||||
|
sipType = sipType_QgsAnnotationLayer3DRenderer;
|
||||||
|
else
|
||||||
|
sipType = nullptr;
|
||||||
|
SIP_END
|
||||||
|
#endif
|
||||||
|
|
||||||
|
public:
|
||||||
|
QgsAnnotationLayer3DRenderer();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the annotation layer associated with the renderer.
|
||||||
|
*
|
||||||
|
* \see layer()
|
||||||
|
*/
|
||||||
|
void setLayer( QgsAnnotationLayer *layer );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the annotation layer associated with the renderer.
|
||||||
|
*
|
||||||
|
* \see setLayer()
|
||||||
|
*/
|
||||||
|
QgsAnnotationLayer *layer() const;
|
||||||
|
|
||||||
|
QString type() const override;
|
||||||
|
QgsAnnotationLayer3DRenderer *clone() const override SIP_FACTORY;
|
||||||
|
Qt3DCore::QEntity *createEntity( Qgs3DMapSettings *map ) const override SIP_SKIP;
|
||||||
|
|
||||||
|
void writeXml( QDomElement &elem, const QgsReadWriteContext &context ) const override;
|
||||||
|
void readXml( const QDomElement &elem, const QgsReadWriteContext &context ) override;
|
||||||
|
void resolveReferences( const QgsProject &project ) override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the altitude clamping method, which determines the vertical position of annotations.
|
||||||
|
*
|
||||||
|
* \see setAltitudeClamping()
|
||||||
|
*/
|
||||||
|
Qgis::AltitudeClamping altitudeClamping() const { return mAltClamping; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the altitude \a clamping method, which determines the vertical position of annotations.
|
||||||
|
*
|
||||||
|
* \see altitudeClamping()
|
||||||
|
*/
|
||||||
|
void setAltitudeClamping( Qgis::AltitudeClamping clamping ) { mAltClamping = clamping; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the z offset, which is a fixed offset amount which should be added to z values for the annotations.
|
||||||
|
*
|
||||||
|
* \see setZOffset()
|
||||||
|
*/
|
||||||
|
double zOffset() const { return mZOffset; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the z \a offset, which is a fixed offset amount which will be added to z values for the annotations.
|
||||||
|
*
|
||||||
|
* \see zOffset()
|
||||||
|
*/
|
||||||
|
void setZOffset( double offset ) { mZOffset = offset; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns TRUE if callout lines are shown, vertically joining the annotations to the terrain.
|
||||||
|
*
|
||||||
|
* \see setShowCalloutLines()
|
||||||
|
*/
|
||||||
|
bool showCalloutLines() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets whether callout lines are shown, vertically joining the annotations to the terrain.
|
||||||
|
*
|
||||||
|
* \see showCalloutLines()
|
||||||
|
*/
|
||||||
|
void setShowCalloutLines( bool show );
|
||||||
|
|
||||||
|
// TODO -- consider exposing via QgsSimpleLineMaterialSettings, for now, for testing only
|
||||||
|
/**
|
||||||
|
* Sets the callout line \a color.
|
||||||
|
*
|
||||||
|
* \see calloutLineColor()
|
||||||
|
* \note Not available in Python bindings
|
||||||
|
*/
|
||||||
|
SIP_SKIP void setCalloutLineColor( const QColor &color );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the callout line color.
|
||||||
|
*
|
||||||
|
* \see setCalloutLineColor()
|
||||||
|
* \note Not available in Python bindings
|
||||||
|
*/
|
||||||
|
SIP_SKIP QColor calloutLineColor() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the callout line \a width.
|
||||||
|
*
|
||||||
|
* \see calloutLineWidth()
|
||||||
|
* \note Not available in Python bindings
|
||||||
|
*/
|
||||||
|
SIP_SKIP void setCalloutLineWidth( double width );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the callout line width.
|
||||||
|
*
|
||||||
|
* \see setCalloutLineWidth()
|
||||||
|
* \note Not available in Python bindings
|
||||||
|
*/
|
||||||
|
SIP_SKIP double calloutLineWidth() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
#ifdef SIP_RUN
|
||||||
|
QgsAnnotationLayer3DRenderer( const QgsAnnotationLayer3DRenderer & );
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static constexpr double DEFAULT_Z_OFFSET = 50;
|
||||||
|
|
||||||
|
QgsMapLayerRef mLayerRef;
|
||||||
|
Qgis::AltitudeClamping mAltClamping = Qgis::AltitudeClamping::Relative;
|
||||||
|
double mZOffset = DEFAULT_Z_OFFSET;
|
||||||
|
bool mShowCalloutLines = true;
|
||||||
|
QColor mCalloutLineColor { 0, 0, 0 };
|
||||||
|
double mCalloutLineWidth = 2;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // QGSANNOTATIONLAYER3DRENDERER_H
|
396
src/3d/qgsannotationlayerchunkloader_p.cpp
Normal file
396
src/3d/qgsannotationlayerchunkloader_p.cpp
Normal file
@ -0,0 +1,396 @@
|
|||||||
|
/***************************************************************************
|
||||||
|
qgsannotationlayerchunkloader_p.cpp
|
||||||
|
--------------------------------------
|
||||||
|
Date : September 2025
|
||||||
|
Copyright : (C) 2025 by Nyall Dawson
|
||||||
|
Email : nyall dot dawson at gmail dot com
|
||||||
|
***************************************************************************
|
||||||
|
* *
|
||||||
|
* This program is free software; you can redistribute it and/or modify *
|
||||||
|
* it under the terms of the GNU General Public License as published by *
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or *
|
||||||
|
* (at your option) any later version. *
|
||||||
|
* *
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
#include "qgsannotationlayerchunkloader_p.h"
|
||||||
|
#include "moc_qgsannotationlayerchunkloader_p.cpp"
|
||||||
|
#include "qgs3dutils.h"
|
||||||
|
#include "qgsannotationitem.h"
|
||||||
|
#include "qgstessellatedpolygongeometry.h"
|
||||||
|
#include "qgschunknode.h"
|
||||||
|
#include "qgseventtracing.h"
|
||||||
|
#include "qgslogger.h"
|
||||||
|
#include "qgsannotationlayer.h"
|
||||||
|
#include "qgsabstract3dsymbol.h"
|
||||||
|
#include "qgsabstractterrainsettings.h"
|
||||||
|
#include "qgsannotationmarkeritem.h"
|
||||||
|
#include "qgsbillboardgeometry.h"
|
||||||
|
#include "qgspoint3dbillboardmaterial.h"
|
||||||
|
#include "qgsgeotransform.h"
|
||||||
|
#include "qgsexpressioncontextutils.h"
|
||||||
|
#include "qgstextureatlasgenerator.h"
|
||||||
|
#include "qgslinevertexdata_p.h"
|
||||||
|
#include "qgslinematerial_p.h"
|
||||||
|
|
||||||
|
#include <QtConcurrent>
|
||||||
|
#include <Qt3DCore/QTransform>
|
||||||
|
|
||||||
|
///@cond PRIVATE
|
||||||
|
|
||||||
|
|
||||||
|
QgsAnnotationLayerChunkLoader::QgsAnnotationLayerChunkLoader( const QgsAnnotationLayerChunkLoaderFactory *factory, QgsChunkNode *node )
|
||||||
|
: QgsChunkLoader( node )
|
||||||
|
, mFactory( factory )
|
||||||
|
, mRenderContext( factory->mRenderContext )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
struct Billboard
|
||||||
|
{
|
||||||
|
QVector3D position;
|
||||||
|
int textureId;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
void QgsAnnotationLayerChunkLoader::start()
|
||||||
|
{
|
||||||
|
QgsChunkNode *node = chunk();
|
||||||
|
if ( node->level() < mFactory->mLeafLevel )
|
||||||
|
{
|
||||||
|
QTimer::singleShot( 0, this, &QgsAnnotationLayerChunkLoader::finished );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QgsAnnotationLayer *layer = mFactory->mLayer;
|
||||||
|
mLayerName = mFactory->mLayer->name();
|
||||||
|
|
||||||
|
// only a subset of data to be queried
|
||||||
|
const QgsRectangle rect = node->box3D().toRectangle();
|
||||||
|
// origin for coordinates of the chunk - it is kind of arbitrary, but it should be
|
||||||
|
// picked so that the coordinates are relatively small to avoid numerical precision issues
|
||||||
|
mChunkOrigin = QgsVector3D( rect.center().x(), rect.center().y(), 0 );
|
||||||
|
|
||||||
|
QgsExpressionContext exprContext;
|
||||||
|
exprContext.appendScopes( QgsExpressionContextUtils::globalProjectLayerScopes( layer ) );
|
||||||
|
mRenderContext.setExpressionContext( exprContext );
|
||||||
|
|
||||||
|
QgsCoordinateTransform layerToMapTransform( layer->crs(), mRenderContext.crs(), mRenderContext.transformContext() );
|
||||||
|
|
||||||
|
QgsRectangle layerExtent;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
layerExtent = layerToMapTransform.transformBoundingBox( rect, Qgis::TransformDirection::Reverse );
|
||||||
|
}
|
||||||
|
catch ( QgsCsException &e )
|
||||||
|
{
|
||||||
|
QgsDebugError( QStringLiteral( "Error transforming annotation layer extent to 3d map extent: %1" ).arg( e.what() ) );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const double zOffset = mFactory->mZOffset;
|
||||||
|
const Qgis::AltitudeClamping altitudeClamping = mFactory->mClamping;
|
||||||
|
bool showCallouts = mFactory->mShowCallouts;
|
||||||
|
|
||||||
|
// see logic from QgsAnnotationLayerRenderer
|
||||||
|
const QStringList itemsList = layer->queryIndex( layerExtent );
|
||||||
|
QSet< QString > itemIds( itemsList.begin(), itemsList.end() );
|
||||||
|
|
||||||
|
// we also have NO choice but to clone ALL non-indexed items (i.e. those with a scale-dependent bounding box)
|
||||||
|
// since these won't be in the layer's spatial index, and it's too expensive to determine their actual bounding box
|
||||||
|
// upfront (we are blocking the main thread right now!)
|
||||||
|
|
||||||
|
// TODO -- come up with some brilliant way to avoid this and also index scale-dependent items ;)
|
||||||
|
itemIds.unite( layer->mNonIndexedItems );
|
||||||
|
|
||||||
|
mItemsToRender.reserve( itemIds.size() );
|
||||||
|
std::transform( itemIds.begin(), itemIds.end(), std::back_inserter( mItemsToRender ), [layer]( const QString &id ) -> std::unique_ptr< QgsAnnotationItem > {
|
||||||
|
return std::unique_ptr< QgsAnnotationItem >( layer->item( id )->clone() );
|
||||||
|
} );
|
||||||
|
|
||||||
|
//
|
||||||
|
// this will be run in a background thread
|
||||||
|
//
|
||||||
|
mFutureWatcher = new QFutureWatcher<void>( this );
|
||||||
|
connect( mFutureWatcher, &QFutureWatcher<void>::finished, this, &QgsChunkQueueJob::finished );
|
||||||
|
|
||||||
|
const QFuture<void> future = QtConcurrent::run( [this, rect, layerToMapTransform, zOffset, altitudeClamping, showCallouts] {
|
||||||
|
const QgsEventTracing::ScopedEvent e( QStringLiteral( "3D" ), QStringLiteral( "Annotation layer chunk load" ) );
|
||||||
|
|
||||||
|
std::vector< Billboard > billboards;
|
||||||
|
billboards.reserve( mItemsToRender.size() );
|
||||||
|
QVector< QImage > textures;
|
||||||
|
textures.reserve( mItemsToRender.size() );
|
||||||
|
|
||||||
|
for ( const std::unique_ptr< QgsAnnotationItem > &item : std::as_const( mItemsToRender ) )
|
||||||
|
{
|
||||||
|
if ( mCanceled )
|
||||||
|
break;
|
||||||
|
|
||||||
|
QgsAnnotationItem *annotation = item.get();
|
||||||
|
|
||||||
|
if ( !annotation->enabled() )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if ( QgsAnnotationMarkerItem *marker = dynamic_cast< QgsAnnotationMarkerItem * >( annotation ) )
|
||||||
|
{
|
||||||
|
if ( marker->symbol() )
|
||||||
|
{
|
||||||
|
QgsPointXY p = marker->geometry();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
const QgsPointXY mapPoint = layerToMapTransform.transform( p );
|
||||||
|
if ( !rect.contains( mapPoint ) )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
double z = 0;
|
||||||
|
const float terrainZ = ( altitudeClamping == Qgis::AltitudeClamping::Absolute && !showCallouts ) ? 0 : mRenderContext.terrainRenderingEnabled() && mRenderContext.terrainGenerator() ? static_cast<float>( mRenderContext.terrainGenerator()->heightAt( mapPoint.x(), mapPoint.y(), mRenderContext ) * mRenderContext.terrainSettings()->verticalScale() )
|
||||||
|
: 0.f;
|
||||||
|
|
||||||
|
switch ( altitudeClamping )
|
||||||
|
{
|
||||||
|
case Qgis::AltitudeClamping::Absolute:
|
||||||
|
z = zOffset;
|
||||||
|
break;
|
||||||
|
case Qgis::AltitudeClamping::Terrain:
|
||||||
|
z = terrainZ;
|
||||||
|
break;
|
||||||
|
case Qgis::AltitudeClamping::Relative:
|
||||||
|
z = terrainZ + zOffset;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
Billboard billboard;
|
||||||
|
billboard.position = ( QgsVector3D( mapPoint.x(), mapPoint.y(), z ) - mChunkOrigin ).toVector3D();
|
||||||
|
billboard.textureId = textures.size();
|
||||||
|
textures.append( QgsPoint3DBillboardMaterial::renderSymbolToImage( marker->symbol(), mRenderContext ) );
|
||||||
|
billboards.emplace_back( std::move( billboard ) );
|
||||||
|
|
||||||
|
if ( showCallouts )
|
||||||
|
{
|
||||||
|
mCalloutLines << QgsLineString( { mapPoint.x(), mapPoint.x() }, { mapPoint.y(), mapPoint.y() }, { terrainZ, z } );
|
||||||
|
}
|
||||||
|
|
||||||
|
mZMax = std::max( mZMax, showCallouts ? std::max( 0.0, z ) : z );
|
||||||
|
mZMin = std::min( mZMin, showCallouts ? std::min( 0.0, z ) : z );
|
||||||
|
}
|
||||||
|
catch ( QgsCsException &e )
|
||||||
|
{
|
||||||
|
QgsDebugError( e.what() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// free memory
|
||||||
|
mItemsToRender.clear();
|
||||||
|
|
||||||
|
const QgsTextureAtlas atlas = QgsTextureAtlasGenerator::createFromImages( textures, 2048 );
|
||||||
|
if ( atlas.isValid() )
|
||||||
|
{
|
||||||
|
mBillboardAtlas = atlas.renderAtlasTexture();
|
||||||
|
mBillboardPositions.reserve( static_cast< int >( billboards.size() ) );
|
||||||
|
for ( Billboard &billboard : billboards )
|
||||||
|
{
|
||||||
|
const QRect textureRect = atlas.rect( billboard.textureId );
|
||||||
|
QgsBillboardGeometry::BillboardAtlasData geometry;
|
||||||
|
geometry.position = billboard.position;
|
||||||
|
geometry.textureAtlasOffset = QVector2D( static_cast< float >( textureRect.left() ) / static_cast< float>( mBillboardAtlas.width() ), 1 - ( static_cast< float >( textureRect.bottom() ) / static_cast< float>( mBillboardAtlas.height() ) ) );
|
||||||
|
geometry.textureAtlasSize = QVector2D( static_cast< float >( textureRect.width() ) / static_cast< float>( mBillboardAtlas.width() ), static_cast< float>( textureRect.height() ) / static_cast< float>( mBillboardAtlas.height() ) );
|
||||||
|
mBillboardPositions.append( geometry );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
QgsDebugError( QStringLiteral( "Error encountered building texture atlas" ) );
|
||||||
|
mBillboardAtlas = QImage();
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
|
||||||
|
// emit finished() as soon as the handler is populated with features
|
||||||
|
mFutureWatcher->setFuture( future );
|
||||||
|
}
|
||||||
|
|
||||||
|
QgsAnnotationLayerChunkLoader::~QgsAnnotationLayerChunkLoader()
|
||||||
|
{
|
||||||
|
if ( mFutureWatcher && !mFutureWatcher->isFinished() )
|
||||||
|
{
|
||||||
|
disconnect( mFutureWatcher, &QFutureWatcher<void>::finished, this, &QgsChunkQueueJob::finished );
|
||||||
|
mFutureWatcher->waitForFinished();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void QgsAnnotationLayerChunkLoader::cancel()
|
||||||
|
{
|
||||||
|
mCanceled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Qt3DCore::QEntity *QgsAnnotationLayerChunkLoader::createEntity( Qt3DCore::QEntity *parent )
|
||||||
|
{
|
||||||
|
if ( mNode->level() < mFactory->mLeafLevel )
|
||||||
|
{
|
||||||
|
Qt3DCore::QEntity *entity = new Qt3DCore::QEntity( parent ); // dummy entity
|
||||||
|
entity->setObjectName( mLayerName + "_CONTAINER_" + mNode->tileId().text() );
|
||||||
|
return entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( mBillboardPositions.empty() )
|
||||||
|
{
|
||||||
|
// an empty node, so we return no entity. This tags the node as having no data and effectively removes it.
|
||||||
|
// we just make sure first that its initial estimated vertical range does not affect its parents' bboxes calculation
|
||||||
|
mNode->setExactBox3D( QgsBox3D() );
|
||||||
|
mNode->updateParentBoundingBoxesRecursively();
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
Qt3DCore::QEntity *entity = new Qt3DCore::QEntity( parent );
|
||||||
|
entity->setObjectName( mLayerName + "_" + mNode->tileId().text() );
|
||||||
|
|
||||||
|
QgsBillboardGeometry *billboardGeometry = new QgsBillboardGeometry();
|
||||||
|
billboardGeometry->setBillboardData( mBillboardPositions );
|
||||||
|
|
||||||
|
Qt3DRender::QGeometryRenderer *billboardGeometryRenderer = new Qt3DRender::QGeometryRenderer;
|
||||||
|
billboardGeometryRenderer->setPrimitiveType( Qt3DRender::QGeometryRenderer::Points );
|
||||||
|
billboardGeometryRenderer->setGeometry( billboardGeometry );
|
||||||
|
billboardGeometryRenderer->setVertexCount( billboardGeometry->count() );
|
||||||
|
|
||||||
|
QgsPoint3DBillboardMaterial *billboardMaterial = new QgsPoint3DBillboardMaterial( QgsPoint3DBillboardMaterial::Mode::AtlasTexture );
|
||||||
|
billboardMaterial->setTexture2DFromImage( mBillboardAtlas );
|
||||||
|
|
||||||
|
QgsGeoTransform *billboardTransform = new QgsGeoTransform;
|
||||||
|
billboardTransform->setGeoTranslation( mChunkOrigin );
|
||||||
|
|
||||||
|
Qt3DCore::QEntity *billboardEntity = new Qt3DCore::QEntity;
|
||||||
|
billboardEntity->addComponent( billboardMaterial );
|
||||||
|
billboardEntity->addComponent( billboardTransform );
|
||||||
|
billboardEntity->addComponent( billboardGeometryRenderer );
|
||||||
|
billboardEntity->setParent( entity );
|
||||||
|
|
||||||
|
if ( mFactory->mShowCallouts )
|
||||||
|
{
|
||||||
|
QgsLineVertexData lineData;
|
||||||
|
lineData.withAdjacency = true;
|
||||||
|
lineData.geocentricCoordinates = false; // mMapSettings->sceneMode() == Qgis::SceneMode::Globe;
|
||||||
|
lineData.init( Qgis::AltitudeClamping::Absolute, Qgis::AltitudeBinding::Vertex, 0, mRenderContext, mChunkOrigin );
|
||||||
|
|
||||||
|
for ( const QgsLineString &line : mCalloutLines )
|
||||||
|
{
|
||||||
|
lineData.addLineString( line, 0, false );
|
||||||
|
}
|
||||||
|
|
||||||
|
QgsLineMaterial *mat = new QgsLineMaterial;
|
||||||
|
mat->setLineColor( mFactory->mCalloutLineColor );
|
||||||
|
mat->setLineWidth( mFactory->mCalloutLineWidth );
|
||||||
|
|
||||||
|
Qt3DCore::QEntity *calloutEntity = new Qt3DCore::QEntity;
|
||||||
|
calloutEntity->setObjectName( parent->objectName() + "_CALLOUTS" );
|
||||||
|
|
||||||
|
// geometry renderer
|
||||||
|
Qt3DRender::QGeometryRenderer *calloutRenderer = new Qt3DRender::QGeometryRenderer;
|
||||||
|
calloutRenderer->setPrimitiveType( Qt3DRender::QGeometryRenderer::LineStripAdjacency );
|
||||||
|
calloutRenderer->setGeometry( lineData.createGeometry( calloutEntity ) );
|
||||||
|
calloutRenderer->setVertexCount( lineData.indexes.count() );
|
||||||
|
calloutRenderer->setPrimitiveRestartEnabled( true );
|
||||||
|
calloutRenderer->setRestartIndexValue( 0 );
|
||||||
|
|
||||||
|
// make entity
|
||||||
|
calloutEntity->addComponent( calloutRenderer );
|
||||||
|
calloutEntity->addComponent( mat );
|
||||||
|
|
||||||
|
calloutEntity->setParent( billboardEntity );
|
||||||
|
}
|
||||||
|
|
||||||
|
// fix the vertical range of the node from the estimated vertical range to the true range
|
||||||
|
if ( mZMin != std::numeric_limits<float>::max() && mZMax != std::numeric_limits<float>::lowest() )
|
||||||
|
{
|
||||||
|
QgsBox3D box = mNode->box3D();
|
||||||
|
box.setZMinimum( mZMin );
|
||||||
|
box.setZMaximum( mZMax );
|
||||||
|
mNode->setExactBox3D( box );
|
||||||
|
mNode->updateParentBoundingBoxesRecursively();
|
||||||
|
}
|
||||||
|
return entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
///////////////
|
||||||
|
|
||||||
|
|
||||||
|
QgsAnnotationLayerChunkLoaderFactory::QgsAnnotationLayerChunkLoaderFactory( const Qgs3DRenderContext &context, QgsAnnotationLayer *layer, int leafLevel, Qgis::AltitudeClamping clamping, double zOffset, bool showCallouts, const QColor &calloutLineColor, double calloutLineWidth, double zMin, double zMax )
|
||||||
|
: mRenderContext( context )
|
||||||
|
, mLayer( layer )
|
||||||
|
, mLeafLevel( leafLevel )
|
||||||
|
, mClamping( clamping )
|
||||||
|
, mZOffset( zOffset )
|
||||||
|
, mShowCallouts( showCallouts )
|
||||||
|
, mCalloutLineColor( calloutLineColor )
|
||||||
|
, mCalloutLineWidth( calloutLineWidth )
|
||||||
|
{
|
||||||
|
if ( context.crs().type() == Qgis::CrsType::Geocentric )
|
||||||
|
{
|
||||||
|
// TODO: add support for handling of annotation layers
|
||||||
|
// (we're using dummy quadtree here to make sure the empty extent does not break the scene completely)
|
||||||
|
QgsDebugError( QStringLiteral( "Annotation layers in globe scenes are not supported yet!" ) );
|
||||||
|
setupQuadtree( QgsBox3D( -1e7, -1e7, -1e7, 1e7, 1e7, 1e7 ), -1, leafLevel );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QgsBox3D rootBox3D( context.extent(), zMin, zMax );
|
||||||
|
// add small padding to avoid clipping of point features located at the edge of the bounding box
|
||||||
|
rootBox3D.grow( 1.0 );
|
||||||
|
setupQuadtree( rootBox3D, -1, leafLevel ); // negative root error means that the node does not contain anything
|
||||||
|
}
|
||||||
|
|
||||||
|
QgsChunkLoader *QgsAnnotationLayerChunkLoaderFactory::createChunkLoader( QgsChunkNode *node ) const
|
||||||
|
{
|
||||||
|
return new QgsAnnotationLayerChunkLoader( this, node );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
///////////////
|
||||||
|
|
||||||
|
|
||||||
|
QgsAnnotationLayerChunkedEntity::QgsAnnotationLayerChunkedEntity( Qgs3DMapSettings *map, QgsAnnotationLayer *layer, Qgis::AltitudeClamping clamping, double zOffset, bool showCallouts, const QColor &calloutLineColor, double calloutLineWidth, double zMin, double zMax )
|
||||||
|
: QgsChunkedEntity( map,
|
||||||
|
-1, // max. allowed screen error (negative tau means that we need to go until leaves are reached)
|
||||||
|
new QgsAnnotationLayerChunkLoaderFactory( Qgs3DRenderContext::fromMapSettings( map ), layer, 3, clamping, zOffset, showCallouts, calloutLineColor, calloutLineWidth, zMin, zMax ), true )
|
||||||
|
{
|
||||||
|
mTransform = new Qt3DCore::QTransform;
|
||||||
|
if ( applyTerrainOffset() )
|
||||||
|
{
|
||||||
|
mTransform->setTranslation( QVector3D( 0.0f, 0.0f, static_cast<float>( map->terrainSettings()->elevationOffset() ) ) );
|
||||||
|
}
|
||||||
|
this->addComponent( mTransform );
|
||||||
|
|
||||||
|
connect( map, &Qgs3DMapSettings::terrainSettingsChanged, this, &QgsAnnotationLayerChunkedEntity::onTerrainElevationOffsetChanged );
|
||||||
|
}
|
||||||
|
|
||||||
|
QgsAnnotationLayerChunkedEntity::~QgsAnnotationLayerChunkedEntity()
|
||||||
|
{
|
||||||
|
// cancel / wait for jobs
|
||||||
|
cancelActiveJobs();
|
||||||
|
}
|
||||||
|
|
||||||
|
// if the AltitudeClamping is `Absolute`, do not apply the offset
|
||||||
|
bool QgsAnnotationLayerChunkedEntity::applyTerrainOffset() const
|
||||||
|
{
|
||||||
|
if ( auto loaderFactory = static_cast<QgsAnnotationLayerChunkLoaderFactory *>( mChunkLoaderFactory ) )
|
||||||
|
{
|
||||||
|
return loaderFactory->mClamping != Qgis::AltitudeClamping::Absolute;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void QgsAnnotationLayerChunkedEntity::onTerrainElevationOffsetChanged()
|
||||||
|
{
|
||||||
|
QgsDebugMsgLevel( QStringLiteral( "QgsAnnotationLayerChunkedEntity::onTerrainElevationOffsetChanged" ), 2 );
|
||||||
|
float newOffset = static_cast<float>( qobject_cast<Qgs3DMapSettings *>( sender() )->terrainSettings()->elevationOffset() );
|
||||||
|
if ( !applyTerrainOffset() )
|
||||||
|
{
|
||||||
|
newOffset = 0.0;
|
||||||
|
}
|
||||||
|
mTransform->setTranslation( QVector3D( 0.0f, 0.0f, newOffset ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// @endcond
|
146
src/3d/qgsannotationlayerchunkloader_p.h
Normal file
146
src/3d/qgsannotationlayerchunkloader_p.h
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
/***************************************************************************
|
||||||
|
qgsannotationlayerchunkloader_p.h
|
||||||
|
--------------------------------------
|
||||||
|
Date : September 2025
|
||||||
|
Copyright : (C) 2025 by Nyall Dawson
|
||||||
|
Email : nyall dot dawson at gmail dot com
|
||||||
|
***************************************************************************
|
||||||
|
* *
|
||||||
|
* This program is free software; you can redistribute it and/or modify *
|
||||||
|
* it under the terms of the GNU General Public License as published by *
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or *
|
||||||
|
* (at your option) any later version. *
|
||||||
|
* *
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
#ifndef QGSANNOTATIONLAYERCHUNKLOADER_P_H
|
||||||
|
#define QGSANNOTATIONLAYERCHUNKLOADER_P_H
|
||||||
|
|
||||||
|
///@cond PRIVATE
|
||||||
|
|
||||||
|
//
|
||||||
|
// W A R N I N G
|
||||||
|
// -------------
|
||||||
|
//
|
||||||
|
// This file is not part of the QGIS API. It exists purely as an
|
||||||
|
// implementation detail. This header file may change from version to
|
||||||
|
// version without notice, or even be removed.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "qgschunkloader.h"
|
||||||
|
#include "qgschunkedentity.h"
|
||||||
|
#include "qgs3drendercontext.h"
|
||||||
|
#include "qgsbillboardgeometry.h"
|
||||||
|
#include <QImage>
|
||||||
|
|
||||||
|
#define SIP_NO_FILE
|
||||||
|
|
||||||
|
class QgsAnnotationLayer;
|
||||||
|
class QgsAnnotationItem;
|
||||||
|
|
||||||
|
namespace Qt3DCore
|
||||||
|
{
|
||||||
|
class QTransform;
|
||||||
|
}
|
||||||
|
|
||||||
|
#include <QFutureWatcher>
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \ingroup qgis_3d
|
||||||
|
* \brief This loader factory is responsible for creation of loaders of QgsAnnotationLayerChunkedEntity.
|
||||||
|
*
|
||||||
|
* \since QGIS 4.0
|
||||||
|
*/
|
||||||
|
class QgsAnnotationLayerChunkLoaderFactory : public QgsQuadtreeChunkLoaderFactory
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
//! Constructs the factory
|
||||||
|
QgsAnnotationLayerChunkLoaderFactory( const Qgs3DRenderContext &context, QgsAnnotationLayer *layer, int leafLevel, Qgis::AltitudeClamping clamping, double zOffset, bool showCallouts, const QColor &calloutLineColor, double calloutLineWidth, double zMin, double zMax );
|
||||||
|
|
||||||
|
//! Creates loader for the given chunk node. Ownership of the returned is passed to the caller.
|
||||||
|
virtual QgsChunkLoader *createChunkLoader( QgsChunkNode *node ) const override;
|
||||||
|
|
||||||
|
Qgs3DRenderContext mRenderContext;
|
||||||
|
QgsAnnotationLayer *mLayer = nullptr;
|
||||||
|
int mLeafLevel = 0;
|
||||||
|
Qgis::AltitudeClamping mClamping = Qgis::AltitudeClamping::Relative;
|
||||||
|
double mZOffset = 0;
|
||||||
|
bool mShowCallouts = false;
|
||||||
|
QColor mCalloutLineColor;
|
||||||
|
double mCalloutLineWidth = 2;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \ingroup qgis_3d
|
||||||
|
* \brief This loader class is responsible for async loading of data for QgsAnnotationLayerChunkedEntity
|
||||||
|
* and creation of final 3D entity from the data previously prepared in a worker thread.
|
||||||
|
*
|
||||||
|
* \since QGIS 4.0
|
||||||
|
*/
|
||||||
|
class QgsAnnotationLayerChunkLoader : public QgsChunkLoader
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
//! Constructs the loader
|
||||||
|
QgsAnnotationLayerChunkLoader( const QgsAnnotationLayerChunkLoaderFactory *factory, QgsChunkNode *node );
|
||||||
|
~QgsAnnotationLayerChunkLoader() override;
|
||||||
|
|
||||||
|
void start() override;
|
||||||
|
virtual void cancel() override;
|
||||||
|
virtual Qt3DCore::QEntity *createEntity( Qt3DCore::QEntity *parent ) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
const QgsAnnotationLayerChunkLoaderFactory *mFactory = nullptr;
|
||||||
|
Qgs3DRenderContext mRenderContext;
|
||||||
|
bool mCanceled = false;
|
||||||
|
QFutureWatcher<void> *mFutureWatcher = nullptr;
|
||||||
|
QString mLayerName;
|
||||||
|
QgsVector3D mChunkOrigin;
|
||||||
|
|
||||||
|
std::vector< std::unique_ptr< QgsAnnotationItem > > mItemsToRender;
|
||||||
|
|
||||||
|
QVector< QgsBillboardGeometry::BillboardAtlasData > mBillboardPositions;
|
||||||
|
QVector< QgsLineString > mCalloutLines;
|
||||||
|
QImage mBillboardAtlas;
|
||||||
|
double mZMin = std::numeric_limits< double >::max();
|
||||||
|
double mZMax = std::numeric_limits< double >::lowest();
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \ingroup qgis_3d
|
||||||
|
* \brief 3D entity used for rendering of annotation layers.
|
||||||
|
*
|
||||||
|
* Internally it uses QgsAnnotationLayerChunkLoaderFactory and
|
||||||
|
* QgsAnnotationLayerChunkLoader to do the actual work
|
||||||
|
* of loading and creating 3D sub-entities for the layer.
|
||||||
|
*
|
||||||
|
* \since QGIS 4.0
|
||||||
|
*/
|
||||||
|
class QgsAnnotationLayerChunkedEntity : public QgsChunkedEntity
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
//! Constructs the entity.
|
||||||
|
explicit QgsAnnotationLayerChunkedEntity( Qgs3DMapSettings *map, QgsAnnotationLayer *layer, Qgis::AltitudeClamping clamping, double zOffset, bool showCallouts, const QColor &calloutLineColor, double calloutLineWidth, double zMin, double zMax );
|
||||||
|
~QgsAnnotationLayerChunkedEntity();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void onTerrainElevationOffsetChanged();
|
||||||
|
|
||||||
|
private:
|
||||||
|
Qt3DCore::QTransform *mTransform = nullptr;
|
||||||
|
|
||||||
|
bool applyTerrainOffset() const;
|
||||||
|
|
||||||
|
friend class TestQgsChunkedEntity;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// @endcond
|
||||||
|
|
||||||
|
#endif // QGSANNOTATIONLAYERCHUNKLOADER_P_H
|
193
src/3d/qgstextureatlasgenerator.cpp
Normal file
193
src/3d/qgstextureatlasgenerator.cpp
Normal file
@ -0,0 +1,193 @@
|
|||||||
|
/***************************************************************************
|
||||||
|
qgstextureatlasgenerator.cpp
|
||||||
|
--------------------------------------
|
||||||
|
Date : September 2025
|
||||||
|
Copyright : (C) 2025 by Nyall Dawson
|
||||||
|
Email : nyall dot dawson at gmail dot com
|
||||||
|
***************************************************************************
|
||||||
|
* *
|
||||||
|
* This program is free software; you can redistribute it and/or modify *
|
||||||
|
* it under the terms of the GNU General Public License as published by *
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or *
|
||||||
|
* (at your option) any later version. *
|
||||||
|
* *
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
#include "qgstextureatlasgenerator.h"
|
||||||
|
#include "qgscolorrampimpl.h"
|
||||||
|
#include <QPainter>
|
||||||
|
|
||||||
|
// rectpack2D library
|
||||||
|
#include <finders_interface.h>
|
||||||
|
|
||||||
|
///@cond PRIVATE
|
||||||
|
|
||||||
|
class QgsTextureRect
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
QgsTextureRect( const rectpack2D::rect_xywh &rect, int id, const QImage &image = QImage() )
|
||||||
|
: rect( rect )
|
||||||
|
, id( id )
|
||||||
|
, image( image )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// get_rect must be implemented for rectpack2D compatibility:
|
||||||
|
auto &get_rect()
|
||||||
|
{
|
||||||
|
return rect;
|
||||||
|
}
|
||||||
|
const auto &get_rect() const
|
||||||
|
{
|
||||||
|
return rect;
|
||||||
|
}
|
||||||
|
|
||||||
|
QRect asQRect() const
|
||||||
|
{
|
||||||
|
return QRect( rect.x, rect.y, rect.w, rect.h );
|
||||||
|
}
|
||||||
|
|
||||||
|
rectpack2D::rect_xywh rect;
|
||||||
|
int id = 0;
|
||||||
|
QImage image;
|
||||||
|
};
|
||||||
|
|
||||||
|
///@endcond
|
||||||
|
|
||||||
|
|
||||||
|
QgsTextureAtlas::QgsTextureAtlas() = default;
|
||||||
|
QgsTextureAtlas::~QgsTextureAtlas() = default;
|
||||||
|
QgsTextureAtlas::QgsTextureAtlas( const QgsTextureAtlas &other ) = default;
|
||||||
|
QgsTextureAtlas &QgsTextureAtlas::operator=( const QgsTextureAtlas &other ) = default;
|
||||||
|
|
||||||
|
int QgsTextureAtlas::count() const
|
||||||
|
{
|
||||||
|
return static_cast< int >( mRects.size() );
|
||||||
|
}
|
||||||
|
|
||||||
|
QRect QgsTextureAtlas::rect( int id ) const
|
||||||
|
{
|
||||||
|
return mRects[id].asQRect();
|
||||||
|
}
|
||||||
|
|
||||||
|
QImage QgsTextureAtlas::renderAtlasTexture() const
|
||||||
|
{
|
||||||
|
if ( mAtlasSize.isEmpty() )
|
||||||
|
return QImage();
|
||||||
|
|
||||||
|
QImage res( mAtlasSize, QImage::Format_ARGB32_Premultiplied );
|
||||||
|
res.fill( Qt::transparent );
|
||||||
|
|
||||||
|
QPainter painter( &res );
|
||||||
|
for ( const QgsTextureRect &rect : mRects )
|
||||||
|
{
|
||||||
|
if ( !rect.image.isNull() )
|
||||||
|
{
|
||||||
|
painter.drawImage( rect.asQRect(), rect.image );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
painter.end();
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
QImage QgsTextureAtlas::renderDebugTexture() const
|
||||||
|
{
|
||||||
|
if ( mAtlasSize.isEmpty() )
|
||||||
|
return QImage();
|
||||||
|
|
||||||
|
QImage res( mAtlasSize, QImage::Format_ARGB32_Premultiplied );
|
||||||
|
res.fill( Qt::transparent );
|
||||||
|
|
||||||
|
QPainter painter( &res );
|
||||||
|
painter.setPen( Qt::NoPen );
|
||||||
|
QgsRandomColorRamp ramp;
|
||||||
|
ramp.setTotalColorCount( static_cast< int >( mRects.size() ) );
|
||||||
|
double index = 0;
|
||||||
|
for ( const QgsTextureRect &rect : mRects )
|
||||||
|
{
|
||||||
|
const QColor color = ramp.color( index / ( static_cast< int >( mRects.size() ) - 1 ) );
|
||||||
|
index += 1;
|
||||||
|
painter.setBrush( QBrush( color ) );
|
||||||
|
painter.drawRect( rect.asQRect() );
|
||||||
|
}
|
||||||
|
painter.end();
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// QgsTextureAtlasGenerator
|
||||||
|
//
|
||||||
|
|
||||||
|
QgsTextureAtlas QgsTextureAtlasGenerator::createFromRects( const QVector<QRect> &rectangles, int maxSide )
|
||||||
|
{
|
||||||
|
std::vector< QgsTextureRect > rects;
|
||||||
|
rects.reserve( rectangles.size() );
|
||||||
|
int index = 0;
|
||||||
|
for ( const QRect &rect : rectangles )
|
||||||
|
{
|
||||||
|
rects.emplace_back( QgsTextureRect( rectpack2D::rect_xywh( 0, 0, rect.width(), rect.height() ), index++ ) );
|
||||||
|
}
|
||||||
|
return generateAtlas( rects, maxSide );
|
||||||
|
}
|
||||||
|
|
||||||
|
QgsTextureAtlas QgsTextureAtlasGenerator::createFromImages( const QVector<QImage> &images, int maxSide )
|
||||||
|
{
|
||||||
|
std::vector< QgsTextureRect > rects;
|
||||||
|
rects.reserve( images.size() );
|
||||||
|
int index = 0;
|
||||||
|
for ( const QImage &image : images )
|
||||||
|
{
|
||||||
|
rects.emplace_back( QgsTextureRect( rectpack2D::rect_xywh( 0, 0, image.width(), image.height() ), index++, image ) );
|
||||||
|
}
|
||||||
|
return generateAtlas( rects, maxSide );
|
||||||
|
}
|
||||||
|
|
||||||
|
QgsTextureAtlas QgsTextureAtlasGenerator::generateAtlas( std::vector< QgsTextureRect > &rects, int maxSide )
|
||||||
|
{
|
||||||
|
using spacesType = rectpack2D::empty_spaces<false, rectpack2D::default_empty_spaces>;
|
||||||
|
|
||||||
|
bool result = true;
|
||||||
|
auto reportSuccessful = []( rectpack2D::rect_xywh & ) {
|
||||||
|
return rectpack2D::callback_result::CONTINUE_PACKING;
|
||||||
|
};
|
||||||
|
|
||||||
|
auto reportUnsuccessful = [&result]( rectpack2D::rect_xywh & ) {
|
||||||
|
result = false;
|
||||||
|
return rectpack2D::callback_result::ABORT_PACKING;
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto discardStep = -4;
|
||||||
|
|
||||||
|
auto byWidth = []( const rectpack2D::rect_xywh *a, const rectpack2D::rect_xywh *b ) {
|
||||||
|
return a->w > b->w;
|
||||||
|
};
|
||||||
|
|
||||||
|
const rectpack2D::rect_wh resultSize = rectpack2D::find_best_packing<spacesType>(
|
||||||
|
rects,
|
||||||
|
rectpack2D::make_finder_input(
|
||||||
|
maxSide,
|
||||||
|
discardStep,
|
||||||
|
reportSuccessful,
|
||||||
|
reportUnsuccessful,
|
||||||
|
rectpack2D::flipping_option::DISABLED
|
||||||
|
),
|
||||||
|
byWidth
|
||||||
|
);
|
||||||
|
|
||||||
|
if ( !result )
|
||||||
|
return QgsTextureAtlas();
|
||||||
|
|
||||||
|
// rectpack2D::find_best_packing will have rearranged rects. Sort it back to the original order
|
||||||
|
// so that we can retreive the results by their original indices.
|
||||||
|
std::sort( rects.begin(), rects.end(), []( const QgsTextureRect &a, const QgsTextureRect &b ) {
|
||||||
|
return a.id < b.id;
|
||||||
|
} );
|
||||||
|
|
||||||
|
QgsTextureAtlas res;
|
||||||
|
res.mRects = std::move( rects );
|
||||||
|
res.mAtlasSize = QSize( resultSize.w, resultSize.h );
|
||||||
|
return res;
|
||||||
|
}
|
174
src/3d/qgstextureatlasgenerator.h
Normal file
174
src/3d/qgstextureatlasgenerator.h
Normal file
@ -0,0 +1,174 @@
|
|||||||
|
/***************************************************************************
|
||||||
|
qgstextureatlasgenerator.h
|
||||||
|
--------------------------------------
|
||||||
|
Date : September 2025
|
||||||
|
Copyright : (C) 2025 by Nyall Dawson
|
||||||
|
Email : nyall dot dawson at gmail dot com
|
||||||
|
***************************************************************************
|
||||||
|
* *
|
||||||
|
* This program is free software; you can redistribute it and/or modify *
|
||||||
|
* it under the terms of the GNU General Public License as published by *
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or *
|
||||||
|
* (at your option) any later version. *
|
||||||
|
* *
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
#ifndef QGSTEXTUREATLASGENERATOR_H
|
||||||
|
#define QGSTEXTUREATLASGENERATOR_H
|
||||||
|
|
||||||
|
#include "qgis_3d.h"
|
||||||
|
#include <vector>
|
||||||
|
#include <QRect>
|
||||||
|
#include <QImage>
|
||||||
|
|
||||||
|
///@cond PRIVATE
|
||||||
|
class QgsTextureRect;
|
||||||
|
///@endcond
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \ingroup qgis_3d
|
||||||
|
* \brief Encapsulates a texture atlas.
|
||||||
|
*
|
||||||
|
* QgsTextureAtlas contains the packed regions for aggregated texture atlases, and optionally the packed
|
||||||
|
* texture map.
|
||||||
|
*
|
||||||
|
* See QgsTextureAtlasGenerator for a class which automatically creates texture atlases.
|
||||||
|
*
|
||||||
|
* \since QGIS 4.0
|
||||||
|
*/
|
||||||
|
class _3D_EXPORT QgsTextureAtlas
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
QgsTextureAtlas();
|
||||||
|
~QgsTextureAtlas();
|
||||||
|
|
||||||
|
QgsTextureAtlas( const QgsTextureAtlas &other );
|
||||||
|
QgsTextureAtlas &operator=( const QgsTextureAtlas &other );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns TRUE if the atlas is valid.
|
||||||
|
*/
|
||||||
|
bool isValid() const { return mAtlasSize.isValid(); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the total size required for the atlas, i.e. the total
|
||||||
|
* size for the packed images and rectangles.
|
||||||
|
*/
|
||||||
|
QSize atlasSize() const { return mAtlasSize; }
|
||||||
|
|
||||||
|
#ifndef SIP_RUN
|
||||||
|
/**
|
||||||
|
* Returns the packed rectangle for the texture with the specified \a index.
|
||||||
|
*/
|
||||||
|
QRect rect( int index ) const;
|
||||||
|
#else
|
||||||
|
/**
|
||||||
|
* Returns the packed rectangle for the texture with the specified \a index.
|
||||||
|
*
|
||||||
|
* \throws IndexError if no texture with the specified index exists.
|
||||||
|
*/
|
||||||
|
QRect rect( int index ) const;
|
||||||
|
//%MethodCode
|
||||||
|
const int count = sipCpp->count();
|
||||||
|
if ( a0 < 0 || a0 >= count )
|
||||||
|
{
|
||||||
|
PyErr_SetString( PyExc_IndexError, QByteArray::number( a0 ) );
|
||||||
|
sipIsErr = 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return sipConvertFromNewType( new QRect( sipCpp->rect( a0 ) ), sipType_QRect, Py_None );
|
||||||
|
}
|
||||||
|
//%End
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders the combined texture atlas, containing all source images.
|
||||||
|
*
|
||||||
|
* \note This may be a null image if the atlas was created with rectangles alone.
|
||||||
|
*/
|
||||||
|
QImage renderAtlasTexture() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders a debug texture.
|
||||||
|
*
|
||||||
|
* The debug texture renders all packed rectangles with a unique color, and can be used
|
||||||
|
* to visualize the solution.
|
||||||
|
*/
|
||||||
|
QImage renderDebugTexture() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the number of textures in the atlas.
|
||||||
|
*/
|
||||||
|
int count() const;
|
||||||
|
|
||||||
|
#ifdef SIP_RUN
|
||||||
|
int __len__() const;
|
||||||
|
% Docstring
|
||||||
|
Returns the number of textures in the atlas.
|
||||||
|
% End
|
||||||
|
//%MethodCode
|
||||||
|
sipRes
|
||||||
|
= sipCpp->count();
|
||||||
|
//% End
|
||||||
|
#endif
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector< QgsTextureRect >
|
||||||
|
mRects;
|
||||||
|
QSize mAtlasSize;
|
||||||
|
|
||||||
|
friend class QgsTextureAtlasGenerator;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \ingroup qgis_3d
|
||||||
|
* \brief Generates texture atlases by efficient packing of multiple input rectangles/images.
|
||||||
|
*
|
||||||
|
* QgsTextureAtlasGenerator can be used to pack either images or raw rectangles. The
|
||||||
|
* static createFromRects() or createFromImages() methods should be called with the
|
||||||
|
* source images or rectangles, which will return a QgsTextureAtlas containing the results.
|
||||||
|
*
|
||||||
|
* \since QGIS 4.0
|
||||||
|
*/
|
||||||
|
class _3D_EXPORT QgsTextureAtlasGenerator
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Creates a texture atlas for a set of \a rectangles.
|
||||||
|
*
|
||||||
|
* This method should be used when the generator is used to pack rectangle shapes only.
|
||||||
|
* No image will be associated with the rectangle.
|
||||||
|
*
|
||||||
|
* The \a maxSide argument specifies the maximum permitted side size for the atlas.
|
||||||
|
* The calculated solution can only be less than or equal to this size - if it cannot fit,
|
||||||
|
* then algorithm will gracefully fail and return an invalid QgsTextureAtlas.
|
||||||
|
*
|
||||||
|
* \see createFromImages()
|
||||||
|
*/
|
||||||
|
static QgsTextureAtlas createFromRects( const QVector< QRect > &rectangles, int maxSide = 1000 );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a texture atlas for a set of \a images.
|
||||||
|
*
|
||||||
|
* The \a maxSide argument specifies the maximum permitted side size for the atlas.
|
||||||
|
* The calculated solution can only be less than or equal to this size - if it cannot fit,
|
||||||
|
* then algorithm will gracefully fail and return an invalid QgsTextureAtlas.
|
||||||
|
*
|
||||||
|
* \see createFromRects()
|
||||||
|
*/
|
||||||
|
static QgsTextureAtlas createFromImages( const QVector< QImage > &images, int maxSide = 1000 );
|
||||||
|
|
||||||
|
private:
|
||||||
|
/**
|
||||||
|
* Generates the packing solution for a set of texture \a rects.
|
||||||
|
*
|
||||||
|
* The \a maxSide argument specifies the maximum permitted side size for the atlas.
|
||||||
|
* The calculated solution can only be less than or equal to this size - if it cannot fit,
|
||||||
|
* then algorithm will gracefully fail and return an invalid QgsTextureAtlas.
|
||||||
|
*/
|
||||||
|
static QgsTextureAtlas generateAtlas( std::vector< QgsTextureRect > &rects, int maxSide );
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // QGSTEXTUREATLASGENERATOR_H
|
@ -22,6 +22,7 @@
|
|||||||
#include "qgscesiumutils.h"
|
#include "qgscesiumutils.h"
|
||||||
#include "qgscoordinatetransform.h"
|
#include "qgscoordinatetransform.h"
|
||||||
#include "qgsgeotransform.h"
|
#include "qgsgeotransform.h"
|
||||||
|
#include "qgsgltfutils.h"
|
||||||
#include "qgsgltf3dutils.h"
|
#include "qgsgltf3dutils.h"
|
||||||
#include "qgsquantizedmeshtiles.h"
|
#include "qgsquantizedmeshtiles.h"
|
||||||
#include "qgsraycastingutils_p.h"
|
#include "qgsraycastingutils_p.h"
|
||||||
@ -124,7 +125,7 @@ void QgsTiledSceneChunkLoader::start()
|
|||||||
errors.append( QStringLiteral( "Failed to parse tile from '%1'" ).arg( uri ) );
|
errors.append( QStringLiteral( "Failed to parse tile from '%1'" ).arg( uri ) );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if ( format == "cesiumtiles" )
|
else if ( format == QLatin1String( "cesiumtiles" ) )
|
||||||
{
|
{
|
||||||
const QgsCesiumUtils::TileContents tileContent = QgsCesiumUtils::extractGltfFromTileContent( content );
|
const QgsCesiumUtils::TileContents tileContent = QgsCesiumUtils::extractGltfFromTileContent( content );
|
||||||
if ( tileContent.gltf.isEmpty() )
|
if ( tileContent.gltf.isEmpty() )
|
||||||
@ -132,6 +133,21 @@ void QgsTiledSceneChunkLoader::start()
|
|||||||
entityTransform.tileTransform.translate( tileContent.rtcCenter );
|
entityTransform.tileTransform.translate( tileContent.rtcCenter );
|
||||||
mEntity = QgsGltf3DUtils::gltfToEntity( tileContent.gltf, entityTransform, uri, &errors );
|
mEntity = QgsGltf3DUtils::gltfToEntity( tileContent.gltf, entityTransform, uri, &errors );
|
||||||
}
|
}
|
||||||
|
else if ( format == QLatin1String( "draco" ) )
|
||||||
|
{
|
||||||
|
QgsGltfUtils::I3SNodeContext i3sContext;
|
||||||
|
i3sContext.initFromTile( tile, mFactory.mLayerCrs, mFactory.mBoundsTransform.sourceCrs(), mFactory.mRenderContext.transformContext() );
|
||||||
|
|
||||||
|
QString dracoLoadError;
|
||||||
|
tinygltf::Model model;
|
||||||
|
if ( !QgsGltfUtils::loadDracoModel( content, i3sContext, model, &dracoLoadError ) )
|
||||||
|
{
|
||||||
|
errors.append( dracoLoadError );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mEntity = QgsGltf3DUtils::parsedGltfToEntity( model, entityTransform, QString(), &errors );
|
||||||
|
}
|
||||||
else
|
else
|
||||||
return; // unsupported tile content type
|
return; // unsupported tile content type
|
||||||
|
|
||||||
@ -173,11 +189,19 @@ Qt3DCore::QEntity *QgsTiledSceneChunkLoader::createEntity( Qt3DCore::QEntity *pa
|
|||||||
|
|
||||||
///
|
///
|
||||||
|
|
||||||
QgsTiledSceneChunkLoaderFactory::QgsTiledSceneChunkLoaderFactory( const Qgs3DRenderContext &context, const QgsTiledSceneIndex &index, QgsCoordinateReferenceSystem tileCrs, double zValueScale, double zValueOffset )
|
QgsTiledSceneChunkLoaderFactory::QgsTiledSceneChunkLoaderFactory(
|
||||||
|
const Qgs3DRenderContext &context,
|
||||||
|
const QgsTiledSceneIndex &index,
|
||||||
|
QgsCoordinateReferenceSystem tileCrs,
|
||||||
|
QgsCoordinateReferenceSystem layerCrs,
|
||||||
|
double zValueScale,
|
||||||
|
double zValueOffset
|
||||||
|
)
|
||||||
: mRenderContext( context )
|
: mRenderContext( context )
|
||||||
, mIndex( index )
|
, mIndex( index )
|
||||||
, mZValueScale( zValueScale )
|
, mZValueScale( zValueScale )
|
||||||
, mZValueOffset( zValueOffset )
|
, mZValueOffset( zValueOffset )
|
||||||
|
, mLayerCrs( layerCrs )
|
||||||
{
|
{
|
||||||
mBoundsTransform = QgsCoordinateTransform( tileCrs, context.crs(), context.transformContext() );
|
mBoundsTransform = QgsCoordinateTransform( tileCrs, context.crs(), context.transformContext() );
|
||||||
}
|
}
|
||||||
@ -346,8 +370,17 @@ void QgsTiledSceneChunkLoaderFactory::prepareChildren( QgsChunkNode *node )
|
|||||||
|
|
||||||
///
|
///
|
||||||
|
|
||||||
QgsTiledSceneLayerChunkedEntity::QgsTiledSceneLayerChunkedEntity( Qgs3DMapSettings *map, const QgsTiledSceneIndex &index, QgsCoordinateReferenceSystem tileCrs, double maximumScreenError, bool showBoundingBoxes, double zValueScale, double zValueOffset )
|
QgsTiledSceneLayerChunkedEntity::QgsTiledSceneLayerChunkedEntity(
|
||||||
: QgsChunkedEntity( map, maximumScreenError, new QgsTiledSceneChunkLoaderFactory( Qgs3DRenderContext::fromMapSettings( map ), index, tileCrs, zValueScale, zValueOffset ), true )
|
Qgs3DMapSettings *map,
|
||||||
|
const QgsTiledSceneIndex &index,
|
||||||
|
QgsCoordinateReferenceSystem tileCrs,
|
||||||
|
QgsCoordinateReferenceSystem layerCrs,
|
||||||
|
double maximumScreenError,
|
||||||
|
bool showBoundingBoxes,
|
||||||
|
double zValueScale,
|
||||||
|
double zValueOffset
|
||||||
|
)
|
||||||
|
: QgsChunkedEntity( map, maximumScreenError, new QgsTiledSceneChunkLoaderFactory( Qgs3DRenderContext::fromMapSettings( map ), index, tileCrs, layerCrs, zValueScale, zValueOffset ), true )
|
||||||
, mIndex( index )
|
, mIndex( index )
|
||||||
{
|
{
|
||||||
setShowBoundingBoxes( showBoundingBoxes );
|
setShowBoundingBoxes( showBoundingBoxes );
|
||||||
|
@ -84,8 +84,12 @@ class QgsTiledSceneChunkLoaderFactory : public QgsChunkLoaderFactory
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
QgsTiledSceneChunkLoaderFactory(
|
QgsTiledSceneChunkLoaderFactory(
|
||||||
const Qgs3DRenderContext &context, const QgsTiledSceneIndex &index, QgsCoordinateReferenceSystem tileCrs,
|
const Qgs3DRenderContext &context,
|
||||||
double zValueScale, double zValueOffset
|
const QgsTiledSceneIndex &index,
|
||||||
|
QgsCoordinateReferenceSystem tileCrs,
|
||||||
|
QgsCoordinateReferenceSystem layerCrs,
|
||||||
|
double zValueScale,
|
||||||
|
double zValueOffset
|
||||||
);
|
);
|
||||||
|
|
||||||
virtual QgsChunkLoader *createChunkLoader( QgsChunkNode *node ) const override;
|
virtual QgsChunkLoader *createChunkLoader( QgsChunkNode *node ) const override;
|
||||||
@ -104,6 +108,7 @@ class QgsTiledSceneChunkLoaderFactory : public QgsChunkLoaderFactory
|
|||||||
double mZValueScale = 1.0;
|
double mZValueScale = 1.0;
|
||||||
double mZValueOffset = 0;
|
double mZValueOffset = 0;
|
||||||
QgsCoordinateTransform mBoundsTransform;
|
QgsCoordinateTransform mBoundsTransform;
|
||||||
|
QgsCoordinateReferenceSystem mLayerCrs;
|
||||||
QSet<long long> mPendingHierarchyFetches;
|
QSet<long long> mPendingHierarchyFetches;
|
||||||
QSet<long long> mFutureHierarchyFetches;
|
QSet<long long> mFutureHierarchyFetches;
|
||||||
};
|
};
|
||||||
@ -123,7 +128,7 @@ class QgsTiledSceneLayerChunkedEntity : public QgsChunkedEntity
|
|||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
explicit QgsTiledSceneLayerChunkedEntity( Qgs3DMapSettings *map, const QgsTiledSceneIndex &index, QgsCoordinateReferenceSystem tileCrs, double maximumScreenError, bool showBoundingBoxes, double zValueScale, double zValueOffset );
|
explicit QgsTiledSceneLayerChunkedEntity( Qgs3DMapSettings *map, const QgsTiledSceneIndex &index, QgsCoordinateReferenceSystem tileCrs, QgsCoordinateReferenceSystem layerCrs, double maximumScreenError, bool showBoundingBoxes, double zValueScale, double zValueOffset );
|
||||||
|
|
||||||
~QgsTiledSceneLayerChunkedEntity();
|
~QgsTiledSceneLayerChunkedEntity();
|
||||||
|
|
||||||
|
@ -66,7 +66,7 @@ Qt3DCore::QEntity *QgsTiledSceneLayer3DRenderer::createEntity( Qgs3DMapSettings
|
|||||||
|
|
||||||
QgsTiledSceneIndex index = tsl->dataProvider()->index();
|
QgsTiledSceneIndex index = tsl->dataProvider()->index();
|
||||||
|
|
||||||
return new QgsTiledSceneLayerChunkedEntity( map, index, tsl->dataProvider()->sceneCrs(), maximumScreenError(), showBoundingBoxes(), qgis::down_cast<const QgsTiledSceneLayerElevationProperties *>( tsl->elevationProperties() )->zScale(), qgis::down_cast<const QgsTiledSceneLayerElevationProperties *>( tsl->elevationProperties() )->zOffset() );
|
return new QgsTiledSceneLayerChunkedEntity( map, index, tsl->dataProvider()->sceneCrs(), tsl->dataProvider()->crs(), maximumScreenError(), showBoundingBoxes(), qgis::down_cast<const QgsTiledSceneLayerElevationProperties *>( tsl->elevationProperties() )->zScale(), qgis::down_cast<const QgsTiledSceneLayerElevationProperties *>( tsl->elevationProperties() )->zOffset() );
|
||||||
}
|
}
|
||||||
|
|
||||||
void QgsTiledSceneLayer3DRenderer::writeXml( QDomElement &elem, const QgsReadWriteContext &context ) const
|
void QgsTiledSceneLayer3DRenderer::writeXml( QDomElement &elem, const QgsReadWriteContext &context ) const
|
||||||
|
@ -296,7 +296,7 @@ QgsPointCloudLayer *QgsPdalAlgorithmBase::parameterAsPointCloudLayer( const QVar
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
// if COPC provider, return as it is
|
// if COPC provider, return as it is
|
||||||
if ( layer->dataProvider()->name() == QStringLiteral( "copc" ) )
|
if ( layer->dataProvider()->name() == QLatin1String( "copc" ) )
|
||||||
{
|
{
|
||||||
return layer;
|
return layer;
|
||||||
}
|
}
|
||||||
|
@ -94,7 +94,7 @@ void QgsGeometryCheckAngleAlgorithm::initAlgorithm( const QVariantMap &configura
|
|||||||
QStringLiteral( "ERRORS" ), QObject::tr( "Small angle errors" ), Qgis::ProcessingSourceType::VectorPoint
|
QStringLiteral( "ERRORS" ), QObject::tr( "Small angle errors" ), Qgis::ProcessingSourceType::VectorPoint
|
||||||
) );
|
) );
|
||||||
|
|
||||||
std::unique_ptr<QgsProcessingParameterNumber> tolerance = std::make_unique<QgsProcessingParameterNumber>(
|
auto tolerance = std::make_unique<QgsProcessingParameterNumber>(
|
||||||
QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13
|
QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13
|
||||||
);
|
);
|
||||||
tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced );
|
tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced );
|
||||||
|
@ -97,7 +97,7 @@ void QgsGeometryCheckAreaAlgorithm::initAlgorithm( const QVariantMap &configurat
|
|||||||
QStringLiteral( "OUTPUT" ), QObject::tr( "Small polygons features" ), Qgis::ProcessingSourceType::VectorPolygon, QVariant(), true, false
|
QStringLiteral( "OUTPUT" ), QObject::tr( "Small polygons features" ), Qgis::ProcessingSourceType::VectorPolygon, QVariant(), true, false
|
||||||
) );
|
) );
|
||||||
|
|
||||||
std::unique_ptr<QgsProcessingParameterNumber> tolerance = std::make_unique<QgsProcessingParameterNumber>(
|
auto tolerance = std::make_unique<QgsProcessingParameterNumber>(
|
||||||
QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13
|
QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13
|
||||||
);
|
);
|
||||||
tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced );
|
tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced );
|
||||||
|
@ -98,7 +98,7 @@ void QgsGeometryCheckContainedAlgorithm::initAlgorithm( const QVariantMap &confi
|
|||||||
QStringLiteral( "OUTPUT" ), QObject::tr( "Contained features" ), Qgis::ProcessingSourceType::VectorAnyGeometry, QVariant(), true, false
|
QStringLiteral( "OUTPUT" ), QObject::tr( "Contained features" ), Qgis::ProcessingSourceType::VectorAnyGeometry, QVariant(), true, false
|
||||||
) );
|
) );
|
||||||
|
|
||||||
std::unique_ptr<QgsProcessingParameterNumber> tolerance = std::make_unique<QgsProcessingParameterNumber>(
|
auto tolerance = std::make_unique<QgsProcessingParameterNumber>(
|
||||||
QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13
|
QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -88,7 +88,7 @@ void QgsGeometryCheckDangleAlgorithm::initAlgorithm( const QVariantMap &configur
|
|||||||
QStringLiteral( "OUTPUT" ), QObject::tr( "Dangle-end features" ), Qgis::ProcessingSourceType::VectorLine, QVariant(), true, false
|
QStringLiteral( "OUTPUT" ), QObject::tr( "Dangle-end features" ), Qgis::ProcessingSourceType::VectorLine, QVariant(), true, false
|
||||||
) );
|
) );
|
||||||
|
|
||||||
std::unique_ptr<QgsProcessingParameterNumber> tolerance = std::make_unique<QgsProcessingParameterNumber>(
|
auto tolerance = std::make_unique<QgsProcessingParameterNumber>(
|
||||||
QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13
|
QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13
|
||||||
);
|
);
|
||||||
tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced );
|
tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced );
|
||||||
|
@ -87,7 +87,7 @@ void QgsGeometryCheckDegeneratePolygonAlgorithm::initAlgorithm( const QVariantMa
|
|||||||
QStringLiteral( "OUTPUT" ), QObject::tr( "Degenerate polygons features" ), Qgis::ProcessingSourceType::VectorPolygon, QVariant(), true, false
|
QStringLiteral( "OUTPUT" ), QObject::tr( "Degenerate polygons features" ), Qgis::ProcessingSourceType::VectorPolygon, QVariant(), true, false
|
||||||
) );
|
) );
|
||||||
|
|
||||||
std::unique_ptr<QgsProcessingParameterNumber> tolerance = std::make_unique<QgsProcessingParameterNumber>( QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13 );
|
auto tolerance = std::make_unique<QgsProcessingParameterNumber>( QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13 );
|
||||||
tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced );
|
tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced );
|
||||||
tolerance->setHelp( QObject::tr( "The \"Tolerance\" advanced parameter defines the numerical precision of geometric operations, "
|
tolerance->setHelp( QObject::tr( "The \"Tolerance\" advanced parameter defines the numerical precision of geometric operations, "
|
||||||
"given as an integer n, meaning that any difference smaller than 10⁻ⁿ (in map units) is considered zero." ) );
|
"given as an integer n, meaning that any difference smaller than 10⁻ⁿ (in map units) is considered zero." ) );
|
||||||
|
@ -94,7 +94,7 @@ void QgsGeometryCheckDuplicateAlgorithm::initAlgorithm( const QVariantMap &confi
|
|||||||
QStringLiteral( "OUTPUT" ), QObject::tr( "Duplicate geometries" ), Qgis::ProcessingSourceType::VectorAnyGeometry, QVariant(), true, false
|
QStringLiteral( "OUTPUT" ), QObject::tr( "Duplicate geometries" ), Qgis::ProcessingSourceType::VectorAnyGeometry, QVariant(), true, false
|
||||||
) );
|
) );
|
||||||
|
|
||||||
std::unique_ptr<QgsProcessingParameterNumber> tolerance = std::make_unique<QgsProcessingParameterNumber>(
|
auto tolerance = std::make_unique<QgsProcessingParameterNumber>(
|
||||||
QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13
|
QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13
|
||||||
);
|
);
|
||||||
tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced );
|
tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced );
|
||||||
|
@ -89,7 +89,7 @@ void QgsGeometryCheckDuplicateNodesAlgorithm::initAlgorithm( const QVariantMap &
|
|||||||
QStringLiteral( "OUTPUT" ), QObject::tr( "Duplicated vertices features" ), Qgis::ProcessingSourceType::VectorAnyGeometry, QVariant(), true, false
|
QStringLiteral( "OUTPUT" ), QObject::tr( "Duplicated vertices features" ), Qgis::ProcessingSourceType::VectorAnyGeometry, QVariant(), true, false
|
||||||
) );
|
) );
|
||||||
|
|
||||||
std::unique_ptr<QgsProcessingParameterNumber> tolerance = std::make_unique<QgsProcessingParameterNumber>(
|
auto tolerance = std::make_unique<QgsProcessingParameterNumber>(
|
||||||
QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13
|
QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13
|
||||||
);
|
);
|
||||||
tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced );
|
tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced );
|
||||||
|
@ -92,7 +92,7 @@ void QgsGeometryCheckFollowBoundariesAlgorithm::initAlgorithm( const QVariantMap
|
|||||||
QStringLiteral( "REF_LAYER" ), QObject::tr( "Reference layer" ), QList<int>() << static_cast<int>( Qgis::ProcessingSourceType::VectorPolygon )
|
QStringLiteral( "REF_LAYER" ), QObject::tr( "Reference layer" ), QList<int>() << static_cast<int>( Qgis::ProcessingSourceType::VectorPolygon )
|
||||||
) );
|
) );
|
||||||
|
|
||||||
std::unique_ptr<QgsProcessingParameterNumber> tolerance = std::make_unique<QgsProcessingParameterNumber>(
|
auto tolerance = std::make_unique<QgsProcessingParameterNumber>(
|
||||||
QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13
|
QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13
|
||||||
);
|
);
|
||||||
tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced );
|
tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced );
|
||||||
|
@ -108,7 +108,7 @@ void QgsGeometryCheckGapAlgorithm::initAlgorithm( const QVariantMap &configurati
|
|||||||
QStringLiteral( "OUTPUT" ), QObject::tr( "Gap features" ), Qgis::ProcessingSourceType::VectorPolygon
|
QStringLiteral( "OUTPUT" ), QObject::tr( "Gap features" ), Qgis::ProcessingSourceType::VectorPolygon
|
||||||
) );
|
) );
|
||||||
|
|
||||||
std::unique_ptr<QgsProcessingParameterNumber> tolerance = std::make_unique<QgsProcessingParameterNumber>(
|
auto tolerance = std::make_unique<QgsProcessingParameterNumber>(
|
||||||
QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13
|
QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13
|
||||||
);
|
);
|
||||||
tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced );
|
tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced );
|
||||||
|
@ -94,7 +94,7 @@ void QgsGeometryCheckHoleAlgorithm::initAlgorithm( const QVariantMap &configurat
|
|||||||
QStringLiteral( "OUTPUT" ), QObject::tr( "Polygons with holes" ), Qgis::ProcessingSourceType::VectorPolygon, QVariant(), true, false
|
QStringLiteral( "OUTPUT" ), QObject::tr( "Polygons with holes" ), Qgis::ProcessingSourceType::VectorPolygon, QVariant(), true, false
|
||||||
) );
|
) );
|
||||||
|
|
||||||
std::unique_ptr<QgsProcessingParameterNumber> tolerance = std::make_unique<QgsProcessingParameterNumber>(
|
auto tolerance = std::make_unique<QgsProcessingParameterNumber>(
|
||||||
QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13
|
QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13
|
||||||
);
|
);
|
||||||
tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced );
|
tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced );
|
||||||
|
@ -88,7 +88,7 @@ void QgsGeometryCheckLineIntersectionAlgorithm::initAlgorithm( const QVariantMap
|
|||||||
QStringLiteral( "OUTPUT" ), QObject::tr( "Intersecting feature" ), Qgis::ProcessingSourceType::VectorAnyGeometry, QVariant(), true, false
|
QStringLiteral( "OUTPUT" ), QObject::tr( "Intersecting feature" ), Qgis::ProcessingSourceType::VectorAnyGeometry, QVariant(), true, false
|
||||||
) );
|
) );
|
||||||
|
|
||||||
std::unique_ptr<QgsProcessingParameterNumber> tolerance = std::make_unique<QgsProcessingParameterNumber>(
|
auto tolerance = std::make_unique<QgsProcessingParameterNumber>(
|
||||||
QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13
|
QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13
|
||||||
);
|
);
|
||||||
tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced );
|
tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced );
|
||||||
|
@ -94,7 +94,7 @@ void QgsGeometryCheckLineLayerIntersectionAlgorithm::initAlgorithm( const QVaria
|
|||||||
QStringLiteral( "OUTPUT" ), QObject::tr( "Line intersecting other layer features" ), Qgis::ProcessingSourceType::VectorAnyGeometry, QVariant(), true, false
|
QStringLiteral( "OUTPUT" ), QObject::tr( "Line intersecting other layer features" ), Qgis::ProcessingSourceType::VectorAnyGeometry, QVariant(), true, false
|
||||||
) );
|
) );
|
||||||
|
|
||||||
std::unique_ptr<QgsProcessingParameterNumber> tolerance = std::make_unique<QgsProcessingParameterNumber>(
|
auto tolerance = std::make_unique<QgsProcessingParameterNumber>(
|
||||||
QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13
|
QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13
|
||||||
);
|
);
|
||||||
tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced );
|
tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced );
|
||||||
|
@ -95,7 +95,7 @@ void QgsGeometryCheckMissingVertexAlgorithm::initAlgorithm( const QVariantMap &c
|
|||||||
QStringLiteral( "OUTPUT" ), QObject::tr( "Missing vertices features" ), Qgis::ProcessingSourceType::VectorPolygon, QVariant(), true, false
|
QStringLiteral( "OUTPUT" ), QObject::tr( "Missing vertices features" ), Qgis::ProcessingSourceType::VectorPolygon, QVariant(), true, false
|
||||||
) );
|
) );
|
||||||
|
|
||||||
std::unique_ptr<QgsProcessingParameterNumber> tolerance = std::make_unique<QgsProcessingParameterNumber>(
|
auto tolerance = std::make_unique<QgsProcessingParameterNumber>(
|
||||||
QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13
|
QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13
|
||||||
);
|
);
|
||||||
tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced );
|
tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced );
|
||||||
|
@ -93,7 +93,7 @@ void QgsGeometryCheckMultipartAlgorithm::initAlgorithm( const QVariantMap &confi
|
|||||||
QStringLiteral( "OUTPUT" ), QObject::tr( "One-part geometry features" ), Qgis::ProcessingSourceType::VectorAnyGeometry, QVariant(), true, false
|
QStringLiteral( "OUTPUT" ), QObject::tr( "One-part geometry features" ), Qgis::ProcessingSourceType::VectorAnyGeometry, QVariant(), true, false
|
||||||
) );
|
) );
|
||||||
|
|
||||||
std::unique_ptr<QgsProcessingParameterNumber> tolerance = std::make_unique<QgsProcessingParameterNumber>(
|
auto tolerance = std::make_unique<QgsProcessingParameterNumber>(
|
||||||
QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13
|
QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13
|
||||||
);
|
);
|
||||||
tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced );
|
tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced );
|
||||||
|
@ -92,7 +92,7 @@ void QgsGeometryCheckOverlapAlgorithm::initAlgorithm( const QVariantMap &configu
|
|||||||
QStringLiteral( "MIN_OVERLAP_AREA" ), QObject::tr( "Minimum overlap area" ), Qgis::ProcessingNumberParameterType::Double, 0, false, 0.0
|
QStringLiteral( "MIN_OVERLAP_AREA" ), QObject::tr( "Minimum overlap area" ), Qgis::ProcessingNumberParameterType::Double, 0, false, 0.0
|
||||||
) );
|
) );
|
||||||
|
|
||||||
std::unique_ptr<QgsProcessingParameterNumber> tolerance = std::make_unique<QgsProcessingParameterNumber>(
|
auto tolerance = std::make_unique<QgsProcessingParameterNumber>(
|
||||||
QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13
|
QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13
|
||||||
);
|
);
|
||||||
tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced );
|
tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced );
|
||||||
|
@ -88,7 +88,7 @@ void QgsGeometryCheckPointCoveredByLineAlgorithm::initAlgorithm( const QVariantM
|
|||||||
QStringLiteral( "ERRORS" ), QObject::tr( "Points not covered by a line" ), Qgis::ProcessingSourceType::VectorPoint
|
QStringLiteral( "ERRORS" ), QObject::tr( "Points not covered by a line" ), Qgis::ProcessingSourceType::VectorPoint
|
||||||
) );
|
) );
|
||||||
|
|
||||||
std::unique_ptr<QgsProcessingParameterNumber> tolerance = std::make_unique<QgsProcessingParameterNumber>(
|
auto tolerance = std::make_unique<QgsProcessingParameterNumber>(
|
||||||
QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13
|
QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13
|
||||||
);
|
);
|
||||||
tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced );
|
tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced );
|
||||||
|
@ -88,7 +88,7 @@ void QgsGeometryCheckPointInPolygonAlgorithm::initAlgorithm( const QVariantMap &
|
|||||||
QStringLiteral( "ERRORS" ), QObject::tr( "Points outside polygons errors" ), Qgis::ProcessingSourceType::VectorPoint
|
QStringLiteral( "ERRORS" ), QObject::tr( "Points outside polygons errors" ), Qgis::ProcessingSourceType::VectorPoint
|
||||||
) );
|
) );
|
||||||
|
|
||||||
std::unique_ptr<QgsProcessingParameterNumber> tolerance = std::make_unique<QgsProcessingParameterNumber>(
|
auto tolerance = std::make_unique<QgsProcessingParameterNumber>(
|
||||||
QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13
|
QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13
|
||||||
);
|
);
|
||||||
tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced );
|
tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced );
|
||||||
|
@ -93,7 +93,7 @@ void QgsGeometryCheckSegmentLengthAlgorithm::initAlgorithm( const QVariantMap &c
|
|||||||
QStringLiteral( "MIN_SEGMENT_LENGTH" ), QObject::tr( "Minimum segment length" ), Qgis::ProcessingNumberParameterType::Double, 0, false, 0.0
|
QStringLiteral( "MIN_SEGMENT_LENGTH" ), QObject::tr( "Minimum segment length" ), Qgis::ProcessingNumberParameterType::Double, 0, false, 0.0
|
||||||
) );
|
) );
|
||||||
|
|
||||||
std::unique_ptr<QgsProcessingParameterNumber> tolerance = std::make_unique<QgsProcessingParameterNumber>(
|
auto tolerance = std::make_unique<QgsProcessingParameterNumber>(
|
||||||
QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13
|
QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13
|
||||||
);
|
);
|
||||||
tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced );
|
tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced );
|
||||||
|
@ -89,7 +89,7 @@ void QgsGeometryCheckSelfContactAlgorithm::initAlgorithm( const QVariantMap &con
|
|||||||
QStringLiteral( "OUTPUT" ), QObject::tr( "Self contact features" ), Qgis::ProcessingSourceType::VectorAnyGeometry, QVariant(), true, false
|
QStringLiteral( "OUTPUT" ), QObject::tr( "Self contact features" ), Qgis::ProcessingSourceType::VectorAnyGeometry, QVariant(), true, false
|
||||||
) );
|
) );
|
||||||
|
|
||||||
std::unique_ptr<QgsProcessingParameterNumber> tolerance = std::make_unique<QgsProcessingParameterNumber>(
|
auto tolerance = std::make_unique<QgsProcessingParameterNumber>(
|
||||||
QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13
|
QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13
|
||||||
);
|
);
|
||||||
tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced );
|
tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced );
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user