[qgis] 01/05: Imported Upstream version 2.14.5+dfsg
Bas Couwenberg
sebastic at debian.org
Fri Jul 29 14:56:43 UTC 2016
This is an automated email from the git hooks/post-receive script.
sebastic pushed a commit to branch master
in repository qgis.
commit e410ce68467b9e5e20b9311f465f9e4ee092ee50
Author: Bas Couwenberg <sebastic at xs4all.nl>
Date: Fri Jul 29 14:12:53 2016 +0200
Imported Upstream version 2.14.5+dfsg
---
.travis.yml | 73 ++-
CMakeLists.txt | 2 +-
ChangeLog | 490 +++++++++++++++++++++
ci/travis/linux/after_script.sh | 15 +
ci/travis/linux/before_install.sh | 80 +---
ci/travis/linux/before_script.sh | 15 +
ci/travis/linux/install.sh | 34 +-
ci/travis/linux/qt4/before_install.sh | 24 +
ci/travis/linux/qt4/install.sh | 59 +++
ci/travis/linux/qt4/script.sh | 27 ++
ci/travis/linux/script.sh | 20 +-
ci/travis/osx/before_install.sh | 28 +-
ci/travis/osx/install.sh | 37 +-
ci/travis/osx/script.sh | 21 +-
cmake/SIPMacros.cmake | 6 +
debian/changelog | 10 +-
debian/qgis.install | 1 -
debian/rules | 6 +-
python/PyQt/CMakeLists.txt | 1 +
python/core/composer/qgscomposerlegend.sip | 14 +
python/core/composer/qgscomposition.sip | 5 +
python/core/core.sip | 1 +
python/core/qgsannotation.sip | 64 +++
python/core/qgsapplication.sip | 16 +
python/core/qgsexpressioncontext.sip | 7 +
python/core/qgsnetworkaccessmanager.sip | 2 +-
python/core/qgsproject.sip | 14 +
python/gui/qgsannotationitem.sip | 26 +-
python/plugins/processing/ProcessingPlugin.py | 2 +-
.../plugins/processing/algs/gdal/GdalAlgorithm.py | 5 +-
python/plugins/processing/algs/qgis/Buffer.py | 14 +-
python/plugins/processing/algs/qgis/Difference.py | 22 +-
.../processing/modeler/ModelerParametersDialog.py | 17 +-
python/plugins/processing/tools/dataobjects.py | 18 +-
src/analysis/raster/qgsninecellfilter.cpp | 16 +-
src/analysis/raster/qgsrastercalculator.cpp | 2 +-
src/analysis/raster/qgsrelief.cpp | 45 +-
src/analysis/vector/qgszonalstatistics.cpp | 6 +-
src/app/composer/qgsatlascompositionwidget.cpp | 3 +-
src/app/composer/qgscomposer.cpp | 74 ++--
src/app/composer/qgscomposer.h | 9 +-
src/app/composer/qgscomposeritemwidget.cpp | 26 +-
src/app/composer/qgscomposeritemwidget.h | 3 +-
src/app/composer/qgscomposerlegendwidget.cpp | 21 +
src/app/composer/qgscomposerlegendwidget.h | 1 +
src/app/composer/qgscomposermanager.cpp | 13 +-
src/app/composer/qgscompositionwidget.cpp | 25 +-
src/app/composer/qgscompositionwidget.h | 2 +
src/app/qgisapp.cpp | 6 +-
src/app/qgsannotationwidget.cpp | 22 +-
src/app/qgsannotationwidget.h | 7 +-
src/app/qgsattributetabledialog.cpp | 1 -
src/app/qgsformannotationdialog.cpp | 1 -
src/app/qgshtmlannotationdialog.cpp | 1 -
src/app/qgsoptions.cpp | 28 +-
src/app/qgsprojectproperties.cpp | 3 +-
src/app/qgssvgannotationdialog.cpp | 1 -
src/app/qgstextannotationdialog.cpp | 15 +-
src/app/qgstextannotationdialog.h | 5 +
src/core/CMakeLists.txt | 1 +
src/core/composer/qgsatlascomposition.cpp | 5 +-
src/core/composer/qgscomposerlegend.cpp | 85 +++-
src/core/composer/qgscomposerlegend.h | 23 +
src/core/composer/qgscomposermap.cpp | 66 ++-
src/core/composer/qgscomposermap.h | 5 +-
src/core/composer/qgscomposition.cpp | 5 +
src/core/composer/qgscomposition.h | 5 +
src/core/geometry/qgsgeometry.cpp | 2 +-
src/core/pal/feature.cpp | 13 +-
src/core/pal/layer.cpp | 29 +-
src/core/qgsannotation.h | 90 ++++
src/core/qgsapplication.cpp | 5 +
src/core/qgsapplication.h | 16 +
src/core/qgsexpressioncontext.cpp | 13 +
src/core/qgsexpressioncontext.h | 7 +
src/core/qgsmaplayerregistry.cpp | 35 +-
src/core/qgsmaplayerregistry.h | 4 +-
src/core/qgsnetworkaccessmanager.cpp | 6 +-
src/core/qgsnetworkaccessmanager.h | 2 +-
src/core/qgsofflineediting.cpp | 28 +-
src/core/qgspallabeling.cpp | 7 +-
src/core/qgsproject.cpp | 4 +
src/core/qgsproject.h | 14 +
.../editorwidgets/qgsrelationreferencewidget.cpp | 2 +-
src/gui/qgsannotationitem.cpp | 48 +-
src/gui/qgsannotationitem.h | 36 +-
src/gui/qgscredentialdialog.cpp | 2 +
src/gui/qgsvariableeditorwidget.h | 14 +-
src/plugins/heatmap/heatmap.cpp | 19 +-
.../oracle/ocispatial/qsql_ocispatial.cpp | 2 +-
src/providers/oracle/qgsoracleconn.cpp | 2 +-
src/providers/oracle/qgsoracleconn.h | 6 +-
src/providers/oracle/qgsoracleconnpool.h | 2 +-
src/providers/oracle/qgsoracleprovider.cpp | 14 +-
src/providers/oracle/qgsoracleprovider.h | 1 -
src/providers/wcs/qgswcsprovider.cpp | 20 +-
src/providers/wms/CMakeLists.txt | 6 +
src/providers/wms/qgswmscapabilities.cpp | 11 +
src/providers/wms/qgswmsprovider.cpp | 68 ++-
src/python/qgspythonutilsimpl.cpp | 9 -
src/python/qgspythonutilsimpl.h | 3 -
src/server/qgsowsserver.h | 2 +
src/server/qgswmsprojectparser.cpp | 5 +
src/ui/composer/qgscomposerlegendwidgetbase.ui | 10 +-
src/ui/qgscredentialdialog.ui | 8 +
src/ui/qgsoptionsbase.ui | 2 +-
src/ui/qgsprojectpropertiesbase.ui | 178 ++------
tests/src/analysis/testqgsalignraster.cpp | 4 +-
tests/src/core/testqgsapplication.cpp | 1 +
tests/src/core/testqgscomposition.cpp | 15 +
.../src/core/testqgscoordinatereferencesystem.cpp | 6 +
tests/src/core/testqgsdistancearea.cpp | 3 +-
tests/src/core/testqgsexpressioncontext.cpp | 12 +
tests/src/core/testqgsgeometry.cpp | 16 +
tests/src/core/testqgsproject.cpp | 8 +
tests/src/core/testqgsrasterlayer.cpp | 5 +-
tests/src/providers/CMakeLists.txt | 9 +
tests/src/providers/testqgswmscapabilities.cpp | 70 +++
tests/src/providers/testqgswmsprovider.cpp | 69 +++
tests/src/python/CMakeLists.txt | 1 +
tests/src/python/test_qgscomposerlegend.py | 203 +++++++++
tests/src/python/test_qgspallabeling_tests.py | 16 +
tests/src/python/utilities.py | 12 +
.../expected_composer_legend_mapunits.png | Bin 0 -> 16960 bytes
.../expected_composer_legend_mapunits_mask.png | Bin 0 -> 6230 bytes
.../expected_composer_legend_noresize.png | Bin 0 -> 9350 bytes
.../expected_composer_legend_noresize_mask.png | Bin 0 -> 6483 bytes
.../expected_composer_legend_noresize_crop.png | Bin 0 -> 7177 bytes
...expected_composer_legend_noresize_crop_mask.png | Bin 0 -> 5598 bytes
.../expected_composer_legend_size_content.png | Bin 0 -> 9364 bytes
.../expected_composer_legend_size_content_mask.png | Bin 0 -> 6085 bytes
.../sp_letter_spacing/sp_letter_spacing.png | Bin 0 -> 5217 bytes
.../sp_letter_spacing/sp_letter_spacing_mask.png | Bin 0 -> 1857 bytes
.../sp_word_spacing/sp_word_spacing.png | Bin 0 -> 5316 bytes
.../sp_word_spacing/sp_word_spacing_mask.png | Bin 0 -> 1857 bytes
.../sp_curved_placement_above.png | Bin 13289 -> 12116 bytes
.../sp_curved_placement_above_mask.png | Bin 8892 -> 3494 bytes
.../sp_curved_placement_below.png | Bin 13273 -> 12044 bytes
.../sp_curved_placement_below_mask.png | Bin 9798 -> 3625 bytes
.../sp_curved_placement_online.png | Bin 12247 -> 11014 bytes
.../sp_curved_placement_online_mask.png | Bin 9404 -> 10684 bytes
.../sp_img_letter_spacing.png | Bin 0 -> 5210 bytes
.../sp_img_letter_spacing_mask.png | Bin 0 -> 1868 bytes
.../sp_img_word_spacing/sp_img_word_spacing.png | Bin 0 -> 5305 bytes
.../sp_img_word_spacing_mask.png | Bin 0 -> 1864 bytes
.../sp_pdf_letter_spacing.png | Bin 0 -> 5214 bytes
.../sp_pdf_letter_spacing_mask.png | Bin 0 -> 1893 bytes
.../sp_pdf_word_spacing/sp_pdf_word_spacing.png | Bin 0 -> 5352 bytes
.../sp_pdf_word_spacing_mask.png | Bin 0 -> 1985 bytes
.../sp_svg_letter_spacing.png | Bin 0 -> 5205 bytes
.../sp_svg_letter_spacing_mask.png | Bin 0 -> 2657 bytes
.../sp_svg_word_spacing/sp_svg_word_spacing.png | Bin 0 -> 5331 bytes
.../sp_svg_word_spacing_mask.png | Bin 0 -> 1734 bytes
.../sp_img_curved_placement_above.png | Bin 13289 -> 12116 bytes
.../sp_img_curved_placement_above_mask.png | Bin 12103 -> 3494 bytes
.../sp_img_curved_placement_below.png | Bin 13273 -> 12044 bytes
.../sp_img_curved_placement_below_mask.png | Bin 13105 -> 3625 bytes
.../sp_img_curved_placement_online.png | Bin 12247 -> 11014 bytes
.../sp_img_curved_placement_online_mask.png | Bin 11918 -> 3398 bytes
.../sp_pdf_curved_placement_above.png | Bin 11069 -> 11536 bytes
.../sp_pdf_curved_placement_above_mask.png | Bin 11946 -> 3645 bytes
.../sp_pdf_curved_placement_below.png | Bin 11094 -> 11542 bytes
.../sp_pdf_curved_placement_below_mask.png | Bin 12864 -> 3552 bytes
.../sp_pdf_curved_placement_online.png | Bin 10389 -> 10775 bytes
.../sp_pdf_curved_placement_online_mask.png | Bin 11755 -> 3416 bytes
.../sp_svg_curved_placement_above.png | Bin 13283 -> 12108 bytes
.../sp_svg_curved_placement_above_mask.png | Bin 8829 -> 3503 bytes
.../sp_svg_curved_placement_below.png | Bin 13298 -> 12035 bytes
.../sp_svg_curved_placement_below_mask.png | Bin 9722 -> 3427 bytes
.../sp_svg_curved_placement_online.png | Bin 12239 -> 11028 bytes
.../sp_svg_curved_placement_online_mask.png | Bin 9343 -> 3044 bytes
.../sp_letter_spacing/sp_letter_spacing.png | Bin 0 -> 5214 bytes
.../sp_letter_spacing/sp_letter_spacing_mask.png | Bin 0 -> 1876 bytes
.../sp_word_spacing/sp_word_spacing.png | Bin 0 -> 5316 bytes
.../sp_word_spacing/sp_word_spacing_mask.png | Bin 0 -> 1974 bytes
.../sp_curved_placement_above.png | Bin 13289 -> 12139 bytes
.../sp_curved_placement_above_mask.png | Bin 8892 -> 2680 bytes
.../sp_curved_placement_below.png | Bin 13273 -> 12047 bytes
.../sp_curved_placement_below_mask.png | Bin 9798 -> 2648 bytes
.../sp_curved_placement_online.png | Bin 12247 -> 11016 bytes
.../sp_curved_placement_online_mask.png | Bin 9404 -> 2760 bytes
tests/testdata/provider/GetCapabilities.xml | 188 ++++++++
182 files changed, 2486 insertions(+), 604 deletions(-)
diff --git a/.travis.yml b/.travis.yml
index 6136abe..c85a552 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,23 +1,69 @@
-language: cpp
-
matrix:
- allow_failures:
- - os: osx
+ fast_finish: true
include:
+ # QT4 based build with Python 2.7 // using container based builds and prebuild binary dependencies in osgeo4travis
- os: linux
- sudo: required
- dist: precise
- group: legacy
- compiler: clang
+ language: cpp
+ env:
+ - BUILD=qt4
+ - QT_VERSION=4
+# - LLVM_VERSION=3.8
+ sudo: false
+ cache:
+ apt: true
+ directories:
+ - $HOME/.ccache
+ compiler: gcc
+ addons:
+ postgresql: "9.4"
+ apt:
+ sources:
+# - llvm-toolchain-precise-3.8
+ - ubuntu-toolchain-r-test
+ - george-edison55-precise-backports # doxygen 1.8.3
+ packages:
+ - bison
+ - gcc-6
+ - g++-6
+ - doxygen
+ - flex
+ - flip
+ - libfcgi-dev
+ - libfftw3-3
+ - libpq-dev
+ - libqscintilla2-dev
+ - libqt4-dev
+ - libqt4-opengl-dev
+ - libqt4-sql-sqlite
+ - libqtwebkit-dev
+ - libqwt-dev
+ - libspatialindex-dev
+ - libspatialite-dev
+ - libsqlite3-dev
+ - pkg-config
+ - poppler-utils
+ - pyqt4-dev-tools
+ - python
+ - python-dev
+ - python-numpy
+ - python-pip
+ - python-psycopg2
+ - python-qscintilla2
+ - python-qt4-dev
+ - python-qt4-sql
+ - python-sip
+ - python-sip-dev
+ - txt2tags
+ - xvfb
+ # OSX based build with QT4 and Python 2
- os: osx
- compiler: clang
-
+ env:
+ - BUILD=osx
+ - IGNORE_BUILD_FAILURES=YES
git:
depth: 30
-cache: apt
-
notifications:
irc: "chat.freenode.net#qgis-test"
on_failure: change
@@ -32,9 +78,6 @@ notifications:
on_failure: always # options: [always|never|change] default: always
on_start: never # default: never
-addons:
- postgresql: "9.1"
-
before_install:
- ./ci/travis/${TRAVIS_OS_NAME}/before_install.sh
diff --git a/CMakeLists.txt b/CMakeLists.txt
index afccf48..9af0765 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,6 +1,6 @@
SET(CPACK_PACKAGE_VERSION_MAJOR "2")
SET(CPACK_PACKAGE_VERSION_MINOR "14")
-SET(CPACK_PACKAGE_VERSION_PATCH "4")
+SET(CPACK_PACKAGE_VERSION_PATCH "5")
SET(COMPLETE_VERSION ${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH})
SET(RELEASE_NAME "Essen")
IF (POLICY CMP0048) # in CMake 3.0.0+
diff --git a/ChangeLog b/ChangeLog
index 9c2bfa2..eca3ffd 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,493 @@
+Nyall Dawson <nyall.dawson at gmail.com> 2016-07-27
+
+ Fix indentation:
+
+Nyall Dawson <nyall.dawson at gmail.com> 2016-07-27
+
+ Fix labeling using perimeter with repeating label distance set
+
+ If the visible part of a polygon is clipped and becomes a multipolygon, only
+ one label is plotted on the wrong side of the polygon.
+
+ Settings:
+ Placement: Using Perimeter
+ Allowed positions: Below line / Line orientation dependent position checked
+ Repeat: 100 mm
+
+ Fix #15341
+
+ (cherry-picked from c0b1684058a5acf3ae58ea63bea7b00520e27725)
+
+Nyall Dawson <nyall.dawson at gmail.com> 2016-07-27
+
+ Update composer legend test masks
+
+Nyall Dawson <nyall.dawson at gmail.com> 2016-07-27
+
+ Fix failing distance area test on OSX
+
+ (cherry-picked from af9b4a7f45fcc1f0885c04966d07e60970546489)
+
+Nyall Dawson <nyall.dawson at gmail.com> 2016-07-27
+
+ [travis] Fix OSX build
+
+Nyall Dawson <nyall.dawson at gmail.com> 2016-07-27
+
+ Fix sip coverage test
+
+ By working around doxygen bug where private members are listed
+ as public
+
+Nyall Dawson <nyall.dawson at gmail.com> 2016-07-27
+
+ Add labeling tests for letter/word spacing
+
+ (cherry-picked from 3d6688cce5598d0c09d13f2cd8db30f1f073e928)
+
+Nyall Dawson <nyall.dawson at gmail.com> 2016-07-26
+
+ Update curved label test reference images
+
+ (cherry-picked from 5228de353c84337b3d753fe15c47ee09ecb2643a)
+
+Nyall Dawson <nyall.dawson at gmail.com> 2016-07-27
+
+ [labels] Fix word and letter spacing truncated to integers
+
+ (cherry-picked from 449fcad8ce0808780cf662362cf5c6568abd09bb)
+
+Nyall Dawson <nyall.dawson at gmail.com> 2016-07-27
+
+ [labeling] Curved labels are now angled per character
+
+ ...instead of shifted along base line (fix #15210)
+
+ (cherry-picked from 22fdb6ab9fcb076f3f3e8601fa4bdc7e0894a103)
+
+Alexander Bruy <alexander.bruy at gmail.com> 2016-07-26
+
+ [processing] fix issues with exported layers in GDAL provider
+
+ (cherry picked from commit b3a38f4018e7b34550f39cffa8bf1197f011dc1e)
+
+Nyall Dawson <nyall.dawson at gmail.com> 2016-07-26
+
+ Fix features are not labeled when using merged connected lines and
+ lines are touching but not at endpoints
+
+ (cherry-picked from 9007d5c11b17840e2e06a4a0bd1f403490d5012a)
+
+Nyall Dawson <nyall.dawson at gmail.com> 2016-07-26
+
+ Fix build warnings
+
+Nyall Dawson <nyall.dawson at gmail.com> 2016-07-26
+
+ [composer] Fix editing of map item variables
+
+ On behalf of Faunalia, sponsored by ENEL
+
+ (cherry-picked from 5384e203fb58a7402ed8ff5598257a953171830d)
+
+Nyall Dawson <nyall.dawson at gmail.com> 2016-07-26
+
+ Fix annotation position when maps are rotated, remove hacks
+
+ Adds a new interface class QgsAnnotation, and allows for removal
+ of a bunch of hacks in QgsComposerMap without breaking 2.x API
+
+ (cherry-picked from 0fa6499bef93b2949a7f35d8cfc35a90a353a004)
+
+Nyall Dawson <nyall.dawson at gmail.com> 2016-07-26
+
+ Fix text annotation edit background should match frame background
+
+ Otherwise white text is not visible. Fix #10553.
+
+ (cherry-picked from 76c4cae)
+
+Nyall Dawson <nyall.dawson at gmail.com> 2016-07-26
+
+ Always keep full precision when saving annotation properties
+
+ (cherry-picked from 0554f5656cedb588598ef41204acbff185e08a33)
+
+Nyall Dawson <nyall.dawson at gmail.com> 2016-07-26
+
+ Avoid dialog 'flashing' when opening annotation properties
+
+ (cherry-picked from a798ba0637b25654e323090b4e2ce50ddd7ea3a4)
+
+Nyall Dawson <nyall.dawson at gmail.com> 2016-07-26
+
+ Fix annotation colors are modified when cancel is clicked
+
+ (cherry-picked from 95fd61c7bd1b9619759382a7f546b385ac33bd8d)
+
+Nyall Dawson <nyall.dawson at gmail.com> 2016-07-26
+
+ Make filename in project properties copyable
+
+ (cherry-picked from d56ca40884f360924654e81ac02bd88c7895495c)
+
+Alexander Bruy <alexander.bruy at gmail.com> 2016-07-25
+
+ Revert "[processing] use default models folder when adding model from file (fix #15335)"
+
+ This reverts commit ebd5b0bc3ada82feb65d2ac3932f29b7a7c615b2 cherry-picked by mistake
+
+Alexander Bruy <alexander.bruy at gmail.com> 2016-07-25
+
+ [processing] use default models folder when adding model from file (fix #15335)
+
+ (cherry picked from commit b167c09e4412b1bf8559020a529b80e520b28b22)
+
+Alexander Bruy <alexander.bruy at gmail.com> 2016-07-25
+
+ [processing] different shortcut for commander (fix #15334)
+
+ (cherry picked from commit 4e94963af54894a3811ea8c44cbb6523ceec4ea0)
+
+Juergen E. Fischer <jef at norbit.de> 2016-07-23
+
+ oracle provider: fix binding of output values
+
+ (cherry picked from commit 1368038ca93ce923cae7f507f5f387690a2f3136)
+
+Merge: 2ebd0eb 4f6422a
+Nyall Dawson <nyall.dawson at gmail.com> 2016-07-23
+
+ Merge pull request #3330 from nyalldawson/gdal_tests
+
+ Backport GDAL test fixes to 2.14
+
+Nyall Dawson <nyall.dawson at gmail.com> 2016-03-31
+
+ Use average mode for align raster downsample tests
+
+ GDAL 2.0 changed (fixed) the bilinear downsampling algorithm, so
+ switch to the average algorithm so that test results are the same
+ for GDAL versions >= 2.0 and < 2.0.
+
+Nyall Dawson <nyall.dawson at gmail.com> 2016-03-31
+
+ Fix qgsrasterlayer tests under GDAL >= 2.0
+
+Nyall Dawson <nyall.dawson at gmail.com> 2016-03-31
+
+ Fix qgis_coordinatereferencesystemtest with GDAL >= 2.0
+
+Matthias Kuhn <matthias at opengis.ch> 2016-07-23
+
+ [travis] Use containerized builds (#3327)
+
+ * Add some debug output when GDALRasterIO operations fail
+
+ Or the compiler will complain about unhandled returns
+
+ * [travis] Use containerized builds
+
+Alexander Bruy <alexander.bruy at gmail.com> 2016-07-22
+
+ [processing] add missed import
+
+Alexander Bruy <alexander.bruy at gmail.com> 2016-07-22
+
+ [processing] also fix case without dissolving
+
+ (cherry picked from commit 2fbb617d618c31ca9ba0d412712ca6ac823ace04)
+
+Alexander Bruy <alexander.bruy at gmail.com> 2016-07-22
+
+ [processing] fix buffer tool
+
+ (cherry picked from commit 9976c30c9aa7b0134b7e72e64157ac9fdb0b042f)
+
+volaya <volayaf at gmail.com> 2016-06-01
+
+ [processing] fixed wrong call to splitext in dataobjects.py
+
+ (cherry picked from commit 479ceb36b40407643764586fc5122333b7eb38eb)
+
+ Conflicts:
+python/plugins/processing/tools/dataobjects.py
+
+Patrick Valsecchi <patrick.valsecchi at camptocamp.com> 2016-05-13
+
+ WMS: Better logic to pick the legend URL
+
+ QGIS had two problems:
+ 1) It was using the specified legend URL only if its mime type was matching
+ the layer's mime type. There is no reason for that.
+ 2) When QGIS was using the default layer (empty string), it was not even
+ trying to find out in what style to pick the legend URL.
+
+ (cherry-picked from 69bed218373b3f93671f65bc3d02c45cbf683a48)
+
+Patrick Valsecchi <patrick.valsecchi at camptocamp.com> 2016-05-13
+
+ WMS GetCapabilities: override parent's style if they have the same name
+
+ When there is a layer group with several sub-layers, the group has a
+ "default" style and the sub-layers each have a "default" layer. QGIS
+ was showing two "default" styles for the sub-layers, which would be
+ confusing to the user and could pick the wrong legend for the
+ sub-layer if the user picked the wrong entry (the first one).
+
+ Had to create a static lib for wmsprovider in order to unittest it.
+
+Nyall Dawson <nyall.dawson at gmail.com> 2016-07-19
+
+ [composer] Add a checkbox for legends to prevent automatic resizing
+
+ A new checkbox has been added to the legend settings to control
+ whether or not a legend should be automatically resized to fit
+ its contents.
+
+ If unchecked, then the legend will never resize and instead just
+ stick to whatever size the user has set. Any content which
+ doesn't fit the size is cropped out.
+
+ Refs #10556
+
+ On behalf of Faunalia, sponsored by ENEL
+
+ (cherry-picked from 2f8c6f52073d4c9c77c39fa119f18ef82783e05d)
+
+Nyall Dawson <nyall.dawson at gmail.com> 2016-07-19
+
+ [composer] Fix setting legend content by map not resizing legend
+
+ When a legend was set to filter content by map, it wasn't
+ consistently being resized to fit the legend contents. This caused
+ issues for atlas exports where legends could grow but never
+ shrink.
+
+ Fix #14707
+
+ On behalf of Faunalia, sponsored by ENEL
+
+ (cherry-picked from 4f31ab656ef04c78d92fce2f1a68833043adb456)
+
+Nyall Dawson <nyall.dawson at gmail.com> 2016-07-19
+
+ [composer] Fix initial size of legend is wrong if symbol size in
+ map units is used (fix #11921)
+
+ On behalf of Faunalia, sponsored by ENEL
+
+ (cherry-picked from 93f2eec711f2d3e1593f497db581a7e6973cfcc9)
+
+Nyall Dawson <nyall.dawson at gmail.com> 2016-07-19
+
+ Avoid some unnecessary composer legend updates
+
+ (cherry-picked from aaa654fba9a79b8842ada7570e653dc8b4f39a97)
+
+Nyall Dawson <nyall.dawson at gmail.com> 2016-07-19
+
+ [oracle] Fix minor Coverity issues
+
+ (cherry-picked from b94fbc0485b3998312749327974bf8e8f49504a9)
+
+Nyall Dawson <nyall.dawson at gmail.com> 2016-07-19
+
+ Fix use of : in label (violation of HIG)
+
+ (cherry-picked from 5a2031349f62d808f78fdc496932d6cc713801d8)
+
+Nyall Dawson <nyall.dawson at gmail.com> 2016-07-19
+
+ Fix crash in QgsGeometry::unaryUnion with empty geometries
+
+ (cherry-picked from b61641dc72b140a02de8eb0636a3817f44b9c8fc)
+
+Nyall Dawson <nyall.dawson at gmail.com> 2016-07-19
+
+ Remove "restart required" from "open table as dock" option
+
+ Since it seems that a restart *isn't* required!
+
+ (cherry-picked from 8943ed7c9f41ef3f7a33f402f709d9bf9156ffd1)
+
+Nyall Dawson <nyall.dawson at gmail.com> 2016-07-19
+
+ [composer] Avoid crash when atlas page name field has spaces
+
+ Fix #15297
+
+ (cherry-picked from 631b5e87c3d4d72b80e6087c48caf7d73a477213)
+
+Nyall Dawson <nyall.dawson at gmail.com> 2016-07-19
+
+ [composer] Load all composition properties from template/duplicate
+
+ Previously some settings where not being correctly restore, eg
+ page size, grid settings, expression variables
+
+ On behalf of Faunalia, sponsored by ENEL
+
+ Fix #8705
+
+ (cherry-picked from 7343b36e2574a9d9b16158911adaaf6b9d3740be)
+
+Nyall Dawson <nyall.dawson at gmail.com> 2016-07-19
+
+ [composer] Simplify and consolidate loading of templates
+
+ On behalf of Faunalia, sponsored by ENEL
+
+ (cherry-picked from 656e56e447c5d75476aba2259ada05bb06699f94)
+
+Nyall Dawson <nyall.dawson at gmail.com> 2016-07-19
+
+ Make sure variable editor widgets always show current variables
+
+ On behalf of Faunalia, sponsored by ENEL
+
+ (cherry-picked from c7ffdfa5e991743e862f10ae2dbec2c05c4b795c)
+
+Merge: dfef36e f99684a
+Alessandro Pasotti <elpaso at itopen.it> 2016-07-18
+
+ Merge pull request #3300 from luca76/patch-2
+
+ Update qgsowsserver.h
+
+Matthias Kuhn <matthias at opengis.ch> 2016-07-12
+
+ [server] Fix crash in WMS server when... bad things happen
+
+ No idea what exactly the reason is, and it was only discovered by
+ countless hours of printf-debugging. So I'm just pushing the fix
+ for everyone else who might be affected.
+
+Matthias Kuhn <matthias at opengis.ch> 2016-07-12
+
+ Fix single process build
+
+ A race condition triggered that sometimes the file
+ output/python/qgis/__init__.py was not created before a python uic
+ compiler started and therefore the required module could not be
+ imported.
+
+ This leads to errors like
+
+ [ 82%] Generating ui_dialogAbout.py
+ Traceback (most recent call last):
+ File "../../../../../scripts/pyuic-wrapper.py", line 26, in <module>
+ import qgis.PyQt.uic.pyuic
+ ImportError: No module named qgis.PyQt.uic.pyuic
+ python/plugins/GdalTools/tools/CMakeFiles/zzz-GdalTools-2-depend.dir/build.make:117:
+ recipe for target 'python/plugins/GdalTools/tools/ui_dialogAbout.py'
+ failed
+ make[2]: *** [python/plugins/GdalTools/tools/ui_dialogAbout.py] Error 1
+ CMakeFiles/Makefile2:5074: recipe for target
+ 'python/plugins/GdalTools/tools/CMakeFiles/zzz-GdalTools-2-depend.dir/all'
+ failed
+ make[1]: ***
+ [python/plugins/GdalTools/tools/CMakeFiles/zzz-GdalTools-2-depend.dir/all]
+ Error 2
+ Makefile:160: recipe for target 'all' failed
+ make: *** [all] Error 2
+
+Luca M <luca76 at users.noreply.github.com> 2016-07-12
+
+ Update qgsowsserver.h
+
+ fix compilation error with HAVE_SERVER_PYTHON_PLUGINS set to OFF in ccmake
+
+Matthias Kuhn <matthias at opengis.ch> 2016-06-06
+
+ Fix on map identification on relation reference widget with complex PK
+
+ References #14882
+
+Matthias Kuhn <matthias at opengis.ch> 2016-06-06
+
+ [offline editing] No reason to crash just because of raster layers
+
+ Fix #14848
+
+Matthias Kuhn <matthias at opengis.ch> 2016-06-26
+
+ Don't put default network cache directory directly in $HOME
+
+ Fix #15111
+
+Matthias Kuhn <matthias at opengis.ch> 2016-06-12
+
+ Show default network cache path in options dialog
+
+Matthias Kuhn <matthias at opengis.ch> 2016-06-12
+
+ Fix network cache configuration
+
+ Fix #14990
+
+Matthias Kuhn <matthias at opengis.ch> 2016-06-13
+
+ Allow docking the attribute table left and right
+
+ Fix #14941
+
+Matthias Kuhn <matthias at opengis.ch> 2016-06-22
+
+ Fix wrong mapping of feature ids in offline editing
+
+ Fix #14727
+
+Matthias Kuhn <matthias at opengis.ch> 2016-06-23
+
+ Fix initial widget focus in credential dialog
+
+Matthias Kuhn <matthias at opengis.ch> 2016-07-02
+
+ Run startup.py only once
+
+ Fix #15189
+
+Matthias Kuhn <matthias at opengis.ch> 2016-07-03
+
+ Safety checks for unregistering map layers from registry
+
+ If a map layer which is registered is deleted outside of the layer
+ registry but not unregistered, the layer registry would still happily
+ return a pointer to this layer if queried with its id.
+
+ Up to now, this caused crashes. Now, the layer will be unregistered and
+ a warning is printed.
+
+ This patch also contains slight improvements to other parts of the map
+ layer registry.
+
+Matthias Kuhn <matthias at opengis.ch> 2016-07-04
+
+ [processing] Difference: don't ignore invalid geometries by default
+
+ Fix #9297
+
+Matthias Kuhn <matthias at opengis.ch> 2016-07-05
+
+ Fix relative paths for offline editing
+
+Juergen E. Fischer <jef at norbit.de> 2016-07-10
+
+ fix 39d6e79
+
+ (cherry picked from commit 2f9ed29d9a5ec7fa79c7f902f036a81c43b8a2da)
+
+Bas Couwenberg <sebastic at xs4all.nl> 2016-07-09
+
+ Fix installation path of scalable icons.
+
+ (cherry picked from commit f5b86230121d533a070748427b613610d308d4d3)
+
+Juergen E. Fischer <jef at norbit.de> 2016-07-08
+
+ Release of 2.14.4
+
Nyall Dawson <nyall.dawson at gmail.com> 2016-07-08
Fix build
diff --git a/ci/travis/linux/after_script.sh b/ci/travis/linux/after_script.sh
index 81864ac..a3a0dab 100755
--- a/ci/travis/linux/after_script.sh
+++ b/ci/travis/linux/after_script.sh
@@ -1 +1,16 @@
+###########################################################################
+# after_script.sh
+# ---------------------
+# Date : September 2015
+# Copyright : (C) 2015 by Matthias Kuhn
+# Email : matthias at opengis dot ch
+###########################################################################
+# #
+# 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. #
+# #
+###########################################################################
+
[ -r /tmp/ctest-important.log ] && cat /tmp/ctest-important.log
diff --git a/ci/travis/linux/before_install.sh b/ci/travis/linux/before_install.sh
index 87f9dea..79a6515 100755
--- a/ci/travis/linux/before_install.sh
+++ b/ci/travis/linux/before_install.sh
@@ -1,65 +1,19 @@
-export DEBIAN_FRONTEND=noninteractive
+#!/bin/bash
+###########################################################################
+# before_install.sh
+# ---------------------
+# Date : August 2015
+# Copyright : (C) 2015 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. #
+# #
+###########################################################################
-sudo add-apt-repository ppa:ubuntugis/ppa -y
-sudo add-apt-repository ppa:ubuntugis/ubuntugis-unstable -y # For postgresql-9.1-postgis-2.1
-sudo add-apt-repository ppa:smspillaz/cmake-3.0.2 -y
-sudo add-apt-repository ppa:kedazo/doxygen-updates-precise -y # For doxygen 1.8.8
-sudo apt-get update -qq
-sudo apt-get install --force-yes --no-install-recommends --no-install-suggests \
- bison \
- cmake \
- cmake-data \
- doxygen \
- flex \
- gdal-bin \
- git \
- graphviz \
- grass-dev \
- libexpat1-dev \
- libfcgi-dev \
- libgdal1-dev \
- libgeos-dev \
- libgsl0-dev \
- libpq-dev \
- libproj-dev \
- libqca2-dev \
- libqca2-plugin-ossl \
- libqscintilla2-dev \
- libqt4-dev \
- libqt4-opengl-dev \
- libqt4-sql-sqlite \
- libqtwebkit-dev \
- libqwt-dev \
- libspatialindex-dev \
- libspatialite-dev \
- libsqlite3-dev \
- lighttpd \
- pkg-config \
- poppler-utils \
- pyqt4-dev-tools \
- python \
- python-dev \
- python-qt4 \
- python-qt4-dev \
- python-qt4-sql \
- python-qscintilla2 \
- python-sip \
- python-sip-dev \
- python-psycopg2 \
- python-numpy \
- python-gdal \
- spawn-fcgi \
- txt2tags \
- xauth \
- xfonts-100dpi \
- xfonts-75dpi \
- xfonts-base \
- xfonts-scalable \
- xvfb \
- python-pip \
- flip \
- jq \
- postgresql-9.1-postgis-2.1/precise # from ubuntugis-unstable, not pgdg
-sudo -H pip install autopep8 # TODO when switching to trusty or above: replace python-pip with python-autopep8
-sudo -H pip install nose2 pyyaml mock
+DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
+${DIR}/qt${QT_VERSION}/before_install.sh
diff --git a/ci/travis/linux/before_script.sh b/ci/travis/linux/before_script.sh
index 9a3abc7..f9c0786 100755
--- a/ci/travis/linux/before_script.sh
+++ b/ci/travis/linux/before_script.sh
@@ -1,3 +1,18 @@
+###########################################################################
+# before_script.sh
+# ---------------------
+# Date : August 2015
+# Copyright : (C) 2015 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. #
+# #
+###########################################################################
+
printf "[qgis_test]\nhost=localhost\ndbname=qgis_test\nuser=postgres" > ~/.pg_service.conf
export PGUSER=postgres
diff --git a/ci/travis/linux/install.sh b/ci/travis/linux/install.sh
index a69641a..43b83d3 100755
--- a/ci/travis/linux/install.sh
+++ b/ci/travis/linux/install.sh
@@ -1,19 +1,19 @@
-mkdir build
-cd build
+#!/bin/bash
+###########################################################################
+# install.sh
+# ---------------------
+# Date : August 2015
+# Copyright : (C) 2015 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. #
+# #
+###########################################################################
-CLANG_WARNINGS="-Wimplicit-fallthrough"
-cmake -DWITH_SERVER=ON \
- -DWITH_STAGED_PLUGINS=ON \
- -DWITH_GRASS=ON \
- -DSUPPRESS_QT_WARNINGS=ON \
- -DENABLE_MODELTEST=ON \
- -DENABLE_PGTEST=ON \
- -DWITH_QWTPOLAR=OFF \
- -DWITH_APIDOC=ON \
- -DWITH_ASTYLE=ON \
- -DWITH_PYSPATIALITE=ON \
- -DGRASS_PREFIX7=/usr/lib/grass70 \
- -DGRASS_INCLUDE_DIR7=/usr/lib/grass70/include \
- -DCXX_EXTRA_FLAGS="$CLANG_WARNINGS" \
- ..
+DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
+${DIR}/qt${QT_VERSION}/install.sh
diff --git a/ci/travis/linux/qt4/before_install.sh b/ci/travis/linux/qt4/before_install.sh
new file mode 100755
index 0000000..b49ef1c
--- /dev/null
+++ b/ci/travis/linux/qt4/before_install.sh
@@ -0,0 +1,24 @@
+###########################################################################
+# before_install.sh
+# ---------------------
+# Date : March 2016
+# Copyright : (C) 2016 by Matthias Kuhn
+# Email : matthias at opengis dot ch
+###########################################################################
+# #
+# 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. #
+# #
+###########################################################################
+
+export DEBIAN_FRONTEND=noninteractive
+
+pushd ${HOME}
+
+curl -L https://github.com/opengisch/osgeo4travis/archive/qt4bin.tar.gz | tar -xzC /home/travis --strip-components=1
+curl -L https://cmake.org/files/v3.5/cmake-3.5.0-Linux-x86_64.tar.gz | tar --strip-components=1 -zxC /home/travis/osgeo4travis
+
+popd
+pip install --user autopep8 nose2 pyyaml mock future
diff --git a/ci/travis/linux/qt4/install.sh b/ci/travis/linux/qt4/install.sh
new file mode 100755
index 0000000..a4b0d1f
--- /dev/null
+++ b/ci/travis/linux/qt4/install.sh
@@ -0,0 +1,59 @@
+###########################################################################
+# install.sh
+# ---------------------
+# Date : March 2016
+# Copyright : (C) 2016 by Matthias Kuhn
+# Email : matthias at opengis dot ch
+###########################################################################
+# #
+# 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. #
+# #
+###########################################################################
+
+mkdir build
+cd build
+
+ln -s ${HOME}/osgeo4travis/bin/ccache ${HOME}/osgeo4travis/bin/clang++-${LLVM_VERSION}
+ln -s ${HOME}/osgeo4travis/bin/ccache ${HOME}/osgeo4travis/bin/clang-${LLVM_VERSION}
+ln -s ${HOME}/osgeo4travis/bin/ccache ${HOME}/osgeo4travis/bin/g++-6
+ln -s ${HOME}/osgeo4travis/bin/ccache ${HOME}/osgeo4travis/bin/gcc-6
+
+ccache -s
+
+#export CXX="clang++-${LLVM_VERSION}"
+#export CC="clang-${LLVM_VERSION}"
+export CXX="g++-6"
+export CC="gcc-6"
+
+export PATH=${HOME}/osgeo4travis/bin:${PATH}
+
+cmake --version
+${CC} --version
+${CXX} --version
+
+# CLANG_WARNINGS="-Wimplicit-fallthrough"
+CLANG_WARNINGS=""
+
+# Include this line for debug reasons
+# -DCMAKE_BUILD_TYPE=RelWithDebInfo \
+#
+cmake \
+ -DCMAKE_PREFIX_PATH=/home/travis/osgeo4travis \
+ -DWITH_STAGED_PLUGINS=ON \
+ -DWITH_GRASS=ON \
+ -DSUPPRESS_QT_WARNINGS=ON \
+ -DENABLE_MODELTEST=ON \
+ -DENABLE_PGTEST=ON \
+ -DWITH_QSPATIALITE=ON \
+ -DWITH_QWTPOLAR=OFF \
+ -DWITH_APIDOC=ON \
+ -DWITH_ASTYLE=ON \
+ -DWITH_SERVER=ON \
+ -DWITH_PYSPATIALITE=ON \
+ -DGRASS_PREFIX7=/usr/lib/grass70 \
+ -DGRASS_INCLUDE_DIR7=/usr/lib/grass70/include \
+ -DCXX_EXTRA_FLAGS="$CLANG_WARNINGS" \
+ ..
diff --git a/ci/travis/linux/qt4/script.sh b/ci/travis/linux/qt4/script.sh
new file mode 100755
index 0000000..6e3a554
--- /dev/null
+++ b/ci/travis/linux/qt4/script.sh
@@ -0,0 +1,27 @@
+###########################################################################
+# script.sh
+# ---------------------
+# Date : March 2016
+# Copyright : (C) 2016 by Matthias Kuhn
+# Email : matthias at opengis dot ch
+###########################################################################
+# #
+# 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. #
+# #
+###########################################################################
+
+export PYTHONPATH=${HOME}/osgeo4travis/lib/python2.7/site-packages/
+export PATH=${HOME}/osgeo4travis/bin:${HOME}/osgeo4travis/sbin:${PATH}
+export LD_LIBRARY_PATH=${HOME}/osgeo4travis/lib
+export CTEST_PARALLEL_LEVEL=1
+export CCACHE_CPP2=yes
+export CCACHE_TEMPDIR=/tmp
+if [ "${TRAVIS_PULL_REQUEST}" != "false" ]; then
+ export CCACHE_READONLY=yes
+ chmod -R ugo-w ~/.ccache
+fi
+
+xvfb-run ctest -V -E 'qgis_openstreetmaptest|qgis_wcsprovidertest' -S ./qgis-test-travis.ctest --output-on-failure
diff --git a/ci/travis/linux/script.sh b/ci/travis/linux/script.sh
index 6c729f1..0bf66b4 100755
--- a/ci/travis/linux/script.sh
+++ b/ci/travis/linux/script.sh
@@ -1 +1,19 @@
-xvfb-run ctest -V -E 'qgis_openstreetmaptest|qgis_wcsprovidertest' -S ./qgis-test-travis.ctest --output-on-failure
+#!/bin/bash
+###########################################################################
+# script.sh
+# ---------------------
+# Date : August 2015
+# Copyright : (C) 2015 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. #
+# #
+###########################################################################
+
+
+DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
+${DIR}/qt${QT_VERSION}/script.sh
diff --git a/ci/travis/osx/before_install.sh b/ci/travis/osx/before_install.sh
index 887126b..8d4ff11 100755
--- a/ci/travis/osx/before_install.sh
+++ b/ci/travis/osx/before_install.sh
@@ -1,7 +1,24 @@
+###########################################################################
+# before_install.sh
+# ---------------------
+# Date : August 2015
+# Copyright : (C) 2015 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. #
+# #
+###########################################################################
+
+# Remove default gdal provided by travis (we will replace it with gdal 2)
+brew remove gdal || true
+
brew tap osgeo/osgeo4mac
brew update
-brew install osgeo/osgeo4mac/qgis-28 --without-postgis --without-postgresql --without-grass --without-gpsbabel --only-dependencies
-brew install qca
+brew install osgeo/osgeo4mac/qgis-214 --without-postgresql --only-dependencies
brew install spawn-fcgi
brew install lighttpd
brew install poppler
@@ -14,8 +31,9 @@ brew ln libxml2 --force
brew ln gettext --force
brew ln libffi --force
-mkdir -p /Users/travis/Library/Python/2.7/lib/python/site-packages
-echo 'import site; site.addsitedir("/usr/local/lib/python2.7/site-packages")' >> /Users/travis/Library/Python/2.7/lib/python/site-packages/homebrew.pth
+mkdir -p ${HOME}/Library/Python/2.7/lib/python/site-packages
+echo 'import site; site.addsitedir("/usr/local/lib/python2.7/site-packages")' >> ${HOME}/Library/Python/2.7/lib/python/site-packages/homebrew.pth
+echo 'import site; site.addsitedir("/usr/local/opt/gdal-20/lib/python2.7/site-packages")' >> ${HOME}/Library/Python/2.7/lib/python/site-packages/gdal2.pth
# Needed for Processing
-pip install psycopg2 numpy nose2 pyyaml mock
+pip install psycopg2 numpy nose2 pyyaml mock future
diff --git a/ci/travis/osx/install.sh b/ci/travis/osx/install.sh
index dea056f..d778411 100755
--- a/ci/travis/osx/install.sh
+++ b/ci/travis/osx/install.sh
@@ -1,11 +1,36 @@
+###########################################################################
+# install.sh
+# ---------------------
+# Date : August 2015
+# Copyright : (C) 2015 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. #
+# #
+###########################################################################
+
mkdir build
cd build
#no PGTEST for OSX - can't get postgres to start with brew install
#no APIDOC for OSX - doxygen tests and warnings are covered by linux build
#no deprecated-declarations warnings... requires QGIS ported to Cocoa
-cmake -DWITH_SERVER=ON -DWITH_STAGED_PLUGINS=ON -DWITH_GRASS=OFF \
- -DSUPPRESS_QT_WARNINGS=ON -DENABLE_MODELTEST=ON -DENABLE_PGTEST=OFF \
- -DWITH_QWTPOLAR=OFF -DWITH_PYSPATIALITE=ON \
- -DQWT_INCLUDE_DIR=/usr/local/opt/qwt/lib/qwt.framework/Headers/ \
- -DQWT_LIBRARY=/usr/local/opt/qwt/lib/qwt.framework/qwt \
- -DCMAKE_CXX_FLAGS="-Wno-deprecated-declarations" ..
+cmake \
+ -DWITH_SERVER=ON \
+ -DWITH_STAGED_PLUGINS=ON \
+ -DWITH_GRASS=OFF \
+ -DSUPPRESS_SIP_WARNINGS=ON \
+ -DSUPPRESS_QT_WARNINGS=ON \
+ -DENABLE_MODELTEST=ON \
+ -DENABLE_PGTEST=OFF \
+ -DWITH_QWTPOLAR=OFF \
+ -DWITH_PYSPATIALITE=ON \
+ -DQWT_INCLUDE_DIR=/usr/local/opt/qwt/lib/qwt.framework/Headers/ \
+ -DQWT_LIBRARY=/usr/local/opt/qwt/lib/qwt.framework/qwt \
+ -DGDAL_CONFIG=/usr/local/opt/gdal-20/bin/gdal-config \
+ -DGRASS_PREFIX7=/usr/local/opt/grass-70/grass-7.0.4 \
+ -DCMAKE_CXX_FLAGS="-Wno-deprecated-declarations" \
+ ..
diff --git a/ci/travis/osx/script.sh b/ci/travis/osx/script.sh
index 084b71c..a3a2c74 100755
--- a/ci/travis/osx/script.sh
+++ b/ci/travis/osx/script.sh
@@ -1,2 +1,21 @@
-ctest -V -E 'qgis_openstreetmaptest|qgis_wcsprovidertest|PyQgsServer' -S ./qgis-test-travis.ctest --output-on-failure
+###########################################################################
+# script.sh
+# ---------------------
+# Date : August 2015
+# Copyright : (C) 2015 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. #
+# #
+###########################################################################
+
+echo $PATH
+
+export PATH=/usr/bin:${PATH}
+
+ctest -V -E 'qgis_openstreetmaptest|qgis_wcsprovidertest|PyQgsServer|ProcessingGdalAlgorithmsTest|PyQgsOfflineEditingWFS|ProcessingGrass7AlgorithmsImageryTest|ProcessingGrass7AlgorithmsRasterTest|qgis_composerhtmltest' -S ./qgis-test-travis.ctest --output-on-failure
diff --git a/cmake/SIPMacros.cmake b/cmake/SIPMacros.cmake
index e308d04..d026741 100644
--- a/cmake/SIPMacros.cmake
+++ b/cmake/SIPMacros.cmake
@@ -103,6 +103,11 @@ MACRO(ADD_SIP_PYTHON_MODULE MODULE_NAME MODULE_SIP)
SET(SIPCMD ${SIP_BINARY_PATH} ${_sip_tags} -w -e ${_sip_x} ${SIP_EXTRA_OPTIONS} -j ${SIP_CONCAT_PARTS} -c ${CMAKE_CURRENT_BINARY_DIR}/${_module_path} ${_sip_includes} ${_abs_module_sip})
+ SET(SUPPRESS_SIP_WARNINGS FALSE CACHE BOOL "Hide SIP warnings")
+ MARK_AS_ADVANCED(SUPPRESS_SIP_WARNINGS)
+ IF(SUPPRESS_SIP_WARNINGS)
+ SET(SIPCMD ${SIPCMD} 2> /dev/null || true)
+ ENDIF(SUPPRESS_SIP_WARNINGS)
ADD_CUSTOM_COMMAND(
OUTPUT ${_sip_output_files}
@@ -110,6 +115,7 @@ MACRO(ADD_SIP_PYTHON_MODULE MODULE_NAME MODULE_SIP)
COMMAND ${CMAKE_COMMAND} -E touch ${_sip_output_files}
COMMAND ${SIPCMD}
DEPENDS ${_abs_module_sip} ${SIP_EXTRA_FILES_DEPEND}
+ VERBATIM
)
# not sure if type MODULE could be uses anywhere, limit to cygwin for now
IF (CYGWIN OR APPLE)
diff --git a/debian/changelog b/debian/changelog
index da8fb0a..b85a92f 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,8 +1,14 @@
-qgis (2.14.4) UNRELEASED; urgency=medium
+qgis (2.14.5) UNRELEASED; urgency=medium
+
+ * Release of 2.14.5
+
+ -- Jürgen E. Fischer <jef at norbit.de> Fri, 29 Jul 2016 14:03:54 +0200
+
+qgis (2.14.4) unstable; urgency=medium
* Release of 2.14.4
- -- Jürgen E. Fischer <jef at norbit.de> Fri, 08 Jul 2016 14:00:33 +0200
+ -- Jürgen E. Fischer <jef at norbit.de> Fri, 29 Jul 2016 14:03:54 +0200
qgis (2.14.3) unstable; urgency=medium
diff --git a/debian/qgis.install b/debian/qgis.install
index 74169d1..b452705 100644
--- a/debian/qgis.install
+++ b/debian/qgis.install
@@ -20,4 +20,3 @@ usr/share/applications/
usr/share/mime/packages/
usr/share/mimelnk/
usr/share/icons/hicolor/
-usr/share/icons/scalable/
diff --git a/debian/rules b/debian/rules
index 90e72c7..1d87840 100755
--- a/debian/rules
+++ b/debian/rules
@@ -279,9 +279,9 @@ override_dh_auto_install:
install -o root -g root -m 644 $(CURDIR)/debian/qbrowser-icon$${size}.png $(CURDIR)/debian/tmp/usr/share/icons/hicolor/$${size}/apps/qbrowser.png ; \
done
- install -o root -g root -d $(CURDIR)/debian/tmp/usr/share/icons/scalable/apps
- install -o root -g root -m 644 $(CURDIR)/images/icons/qgis_icon.svg $(CURDIR)/debian/tmp/usr/share/icons/scalable/apps/qgis.svg
- install -o root -g root -m 644 $(CURDIR)/images/icons/qbrowser_icon.svg $(CURDIR)/debian/tmp/usr/share/icons/scalable/apps/qbrowser.svg
+ install -o root -g root -d $(CURDIR)/debian/tmp/usr/share/icons/hicolor/scalable/apps
+ install -o root -g root -m 644 $(CURDIR)/images/icons/qgis_icon.svg $(CURDIR)/debian/tmp/usr/share/icons/hicolor/scalable/apps/qgis.svg
+ install -o root -g root -m 644 $(CURDIR)/images/icons/qbrowser_icon.svg $(CURDIR)/debian/tmp/usr/share/icons/hicolor/scalable/apps/qbrowser.svg
# Install desktop files
install -o root -g root -d $(CURDIR)/debian/tmp/usr/share/applications
diff --git a/python/PyQt/CMakeLists.txt b/python/PyQt/CMakeLists.txt
index 80fe79c..1520079 100644
--- a/python/PyQt/CMakeLists.txt
+++ b/python/PyQt/CMakeLists.txt
@@ -21,6 +21,7 @@ SET(PYQT_COMPAT_FILES
)
ADD_CUSTOM_TARGET(pyqtcompat ALL)
+ADD_DEPENDENCIES(pyqtcompat pyutils)
IF(ENABLE_QT5)
SET(PYQT_PREFIX PyQt5)
diff --git a/python/core/composer/qgscomposerlegend.sip b/python/core/composer/qgscomposerlegend.sip
index 43fd587..c306979 100644
--- a/python/core/composer/qgscomposerlegend.sip
+++ b/python/core/composer/qgscomposerlegend.sip
@@ -46,6 +46,20 @@ class QgsComposerLegend : QgsComposerItem
/** Sets item box to the whole content*/
void adjustBoxSize();
+ /** Sets whether the legend should automatically resize to fit its contents.
+ * @param enabled set to false to disable automatic resizing. The legend frame will not
+ * be expanded to fit legend items, and items may be cropped from display.
+ * @see resizeToContents()
+ * @note added in QGIS 3.0
+ */
+ void setResizeToContents( bool enabled );
+
+ /** Returns whether the legend should automatically resize to fit its contents.
+ * @see setResizeToContents()
+ * @note added in QGIS 3.0
+ */
+ bool resizeToContents() const;
+
/** Returns pointer to the legend model*/
//! @deprecated in 2.6 - use modelV2()
QgsLegendModel* model() /Deprecated/;
diff --git a/python/core/composer/qgscomposition.sip b/python/core/composer/qgscomposition.sip
index d3b0771..58b14eb 100644
--- a/python/core/composer/qgscomposition.sip
+++ b/python/core/composer/qgscomposition.sip
@@ -856,4 +856,9 @@ class QgsComposition : QGraphicsScene
/** Is emitted when the composition has an updated status bar message for the composer window*/
void statusMsgChanged( QString message );
+
+ /** Emitted whenever the expression variables stored in the composition have been changed.
+ * @note added in QGIS 3.0
+ */
+ void variablesChanged();
};
diff --git a/python/core/core.sip b/python/core/core.sip
index f54fddd..8b37734 100644
--- a/python/core/core.sip
+++ b/python/core/core.sip
@@ -18,6 +18,7 @@
%Include qgis.sip
+%Include qgsannotation.sip
%Include qgsapplication.sip
%Include qgsattributeaction.sip
%Include qgsbrowsermodel.sip
diff --git a/python/core/qgsannotation.sip b/python/core/qgsannotation.sip
new file mode 100644
index 0000000..76b661a
--- /dev/null
+++ b/python/core/qgsannotation.sip
@@ -0,0 +1,64 @@
+/** \ingroup core
+ * \class QgsAnnotation
+ * \note added in QGIS 3.0
+ *
+ * \brief An interface for annotation items which are drawn over a map.
+ *
+ * QgsAnnotation is an interface class for map annotation items. These annotations can be
+ * drawn within a map, and have either a fixed map position (retrieved using mapPosition())
+ * or are placed relative to the map's frame (retrieved using relativePosition()).
+ * Annotations with a fixed map position also have a corresponding
+ * QgsCoordinateReferenceSystem, which can be determined by calling mapPositionCrs().
+ */
+
+class QgsAnnotation
+{
+%TypeHeaderCode
+#include <qgsannotation.h>
+%End
+ public:
+
+ //! Returns true if the annotation should be shown.
+ virtual bool showItem() const = 0;
+
+ /** Returns true if the annotation is attached to a fixed map position, or
+ * false if the annotation uses a position relative to the current map
+ * extent.
+ * @see mapPosition()
+ * @see relativePositon()
+ */
+ //TODO QGIS 3 - rename to hasFixedMapPosition()
+ virtual bool mapPositionFixed() const = 0;
+
+ /** Returns the map position of the annotation, if it is attached to a fixed map
+ * position.
+ * @see mapPositionFixed()
+ * @see mapPositionCrs()
+ */
+ virtual QgsPoint mapPosition() const;
+
+ /** Returns the CRS of the map position, or an invalid CRS if the annotation does
+ * not have a fixed map position.
+ */
+ virtual QgsCoordinateReferenceSystem mapPositionCrs() const;
+
+ /** Returns the relative position of the annotation, if it is not attached to a fixed map
+ * position. The coordinates in the return point should be between 0 and 1, and represent
+ * the relative percentage for the position compared to the map width and height.
+ * @see mapPositionFixed()
+ */
+ virtual QPointF relativePosition() const;
+
+ /** Returns a scaling factor which should be applied to painters before rendering
+ * the item.
+ */
+ virtual double scaleFactor() const = 0;
+
+ //! deprecated - do not use
+ // TODO QGIS 3.0 - remove
+ virtual void setItemData( int role, const QVariant& value ) = 0;
+
+ //! Paint the annotation to a destination painter
+ virtual void paint( QPainter * painter, const QStyleOptionGraphicsItem * option, QWidget * widget = nullptr ) = 0;
+
+};
diff --git a/python/core/qgsapplication.sip b/python/core/qgsapplication.sip
index 40ead0b..be66870 100644
--- a/python/core/qgsapplication.sip
+++ b/python/core/qgsapplication.sip
@@ -379,7 +379,23 @@ static void qtgui_UpdatePyArgv(PyObject *argvlist, int argc, char **argv)
bool x11EventFilter ( XEvent * event );
%End
+ public slots:
+
+ /** Causes the application instance to emit the settingsChanged() signal. This should
+ * be called whenever global, application-wide settings are altered to advise listeners
+ * that they may need to update their state.
+ * @see settingsChanged()
+ * @note added in QGIS 3.0
+ */
+ void emitSettingsChanged();
+
signals:
//! @note not available in python bindings
// void preNotify( QObject * receiver, QEvent * event, bool * done );
+
+ /** Emitted whenever any global, application-wide settings are changed.
+ * @note added in QGIS 3.0
+ * @see emitSettingsChanged()
+ */
+ void settingsChanged();
};
diff --git a/python/core/qgsexpressioncontext.sip b/python/core/qgsexpressioncontext.sip
index 405a3c1..bb3ee14 100644
--- a/python/core/qgsexpressioncontext.sip
+++ b/python/core/qgsexpressioncontext.sip
@@ -289,6 +289,13 @@ class QgsExpressionContext
*/
int indexOfScope( QgsExpressionContextScope* scope ) const;
+ /** Returns the index of the first scope with a matching name within the context.
+ * @param scopeName name of scope to find
+ * @returns index of scope, or -1 if scope was not found within the context.
+ * @note added in QGIS 3.0
+ */
+ int indexOfScope( const QString& scopeName ) const;
+
/** Returns a list of variables names set by all scopes in the context.
* @returns list of unique variable names
* @see filteredVariableNames
diff --git a/python/core/qgsnetworkaccessmanager.sip b/python/core/qgsnetworkaccessmanager.sip
index 9a15075..65da26a 100644
--- a/python/core/qgsnetworkaccessmanager.sip
+++ b/python/core/qgsnetworkaccessmanager.sip
@@ -61,7 +61,7 @@ class QgsNetworkAccessManager : QNetworkAccessManager
void setupDefaultProxyAndCache();
//! return whether the system proxy should be used
- bool useSystemProxy();
+ bool useSystemProxy() const;
public slots:
/** Send GET request, calls get().
diff --git a/python/core/qgsproject.sip b/python/core/qgsproject.sip
index 845bde4..3982c0a 100644
--- a/python/core/qgsproject.sip
+++ b/python/core/qgsproject.sip
@@ -345,6 +345,20 @@ class QgsProject : QObject
void snapSettingsChanged();
+ /** Emitted whenever the expression variables stored in the project have been changed.
+ * @note added in QGIS 3.0
+ */
+ void variablesChanged();
+
+ public slots:
+
+ /** Causes the project to emit the variablesChanged() signal. This should
+ * be called whenever expression variables related to the project are changed.
+ * @see variablesChanged()
+ * @note added in QGIS 3.0
+ */
+ void emitVariablesChanged();
+
private:
QgsProject(); // private 'cause it's a singleton
diff --git a/python/gui/qgsannotationitem.sip b/python/gui/qgsannotationitem.sip
index 2526e36..c4d9df8 100644
--- a/python/gui/qgsannotationitem.sip
+++ b/python/gui/qgsannotationitem.sip
@@ -8,7 +8,7 @@
#include "qgstextannotationitem.h"
%End
-class QgsAnnotationItem: QgsMapCanvasItem
+class QgsAnnotationItem: QgsMapCanvasItem, QgsAnnotation
{
%TypeHeaderCode
#include <qgsannotationitem.h>
@@ -66,6 +66,12 @@ class QgsAnnotationItem: QgsMapCanvasItem
virtual void setMapPosition( const QgsPoint& pos );
QgsPoint mapPosition() const;
+ virtual QPointF relativePosition() const;
+
+ virtual double scaleFactor() const;
+
+ virtual bool showItem() const;
+
/** Sets the CRS of the map position.
@param crs the CRS to set */
virtual void setMapPositionCrs( const QgsCoordinateReferenceSystem& crs );
@@ -97,18 +103,28 @@ class QgsAnnotationItem: QgsMapCanvasItem
void _writeXML( QDomDocument& doc, QDomElement& itemElem ) const;
void _readXML( const QDomDocument& doc, const QDomElement& annotationElem );
+ virtual void setItemData( int role, const QVariant& value );
+ virtual void paint( QPainter * painter, const QStyleOptionGraphicsItem * option, QWidget * widget = nullptr );
+ void paint( QPainter* painter );
+
protected:
void updateBoundingRect();
/** Check where to attach the balloon connection between frame and map point*/
void updateBalloon();
- void drawFrame( QPainter* p );
- void drawMarkerSymbol( QPainter* p );
- void drawSelectionBoxes( QPainter* p );
+ //! Draws the annotation frame to a destination painter
+ void drawFrame( QPainter* p ) const;
+
+ //! Draws the map position marker symbol to a destination painter
+ void drawMarkerSymbol( QPainter* p ) const;
+
+ //! Draws selection handles around the item
+ void drawSelectionBoxes( QPainter* p ) const;
+
/** Returns frame width in painter units*/
//double scaledFrameWidth( QPainter* p) const;
/** Gets the frame line (0 is the top line, 1 right, 2 bottom, 3 left)*/
- QLineF segment( int index );
+ QLineF segment( int index ) const;
/** Returns a point on the line from startPoint to directionPoint that is a certain distance away from the starting point*/
QPointF pointOnLineWithDistance( QPointF startPoint, QPointF directionPoint, double distance ) const;
/** Returns the symbol size scaled in (mapcanvas) pixels. Used for the counding rect calculation*/
diff --git a/python/plugins/processing/ProcessingPlugin.py b/python/plugins/processing/ProcessingPlugin.py
index 3dd031a..cb0c89c 100644
--- a/python/plugins/processing/ProcessingPlugin.py
+++ b/python/plugins/processing/ProcessingPlugin.py
@@ -117,7 +117,7 @@ class ProcessingPlugin:
self.commanderAction.triggered.connect(self.openCommander)
self.menu.addAction(self.commanderAction)
self.iface.registerMainWindowAction(self.commanderAction,
- self.tr('Ctrl+Alt+M'))
+ self.tr('Ctrl+Alt+D'))
def unload(self):
self.toolbox.setVisible(False)
diff --git a/python/plugins/processing/algs/gdal/GdalAlgorithm.py b/python/plugins/processing/algs/gdal/GdalAlgorithm.py
index 9b65d14..ea4a890 100644
--- a/python/plugins/processing/algs/gdal/GdalAlgorithm.py
+++ b/python/plugins/processing/algs/gdal/GdalAlgorithm.py
@@ -16,8 +16,6 @@
* *
***************************************************************************
"""
-from processing.tools import dataobjects
-
__author__ = 'Victor Olaya'
__date__ = 'August 2012'
@@ -34,6 +32,7 @@ from PyQt4.QtGui import QIcon
from processing.core.GeoAlgorithm import GeoAlgorithm
from processing.algs.gdal.GdalAlgorithmDialog import GdalAlgorithmDialog
from processing.algs.gdal.GdalUtils import GdalUtils
+from processing.tools import dataobjects
pluginPath = os.path.normpath(os.path.join(
os.path.split(os.path.dirname(__file__))[0], os.pardir))
@@ -59,7 +58,7 @@ class GdalAlgorithm(GeoAlgorithm):
c = c.replace(layer.source(), exported)
if os.path.isfile(layer.source()):
fileName = os.path.splitext(os.path.split(layer.source())[1])[0]
- c = c.replace(fileName, exportedFileName)
+ c = c.replace(' ' + fileName + ' ', ' ' + exportedFileName + ' ')
commands[i] = c
GdalUtils.runGdal(commands, progress)
diff --git a/python/plugins/processing/algs/qgis/Buffer.py b/python/plugins/processing/algs/qgis/Buffer.py
index db55b0d..433ae2f 100644
--- a/python/plugins/processing/algs/qgis/Buffer.py
+++ b/python/plugins/processing/algs/qgis/Buffer.py
@@ -57,8 +57,11 @@ def buffering(progress, writer, distance, field, useField, layer, dissolve,
value = distance
inGeom = QgsGeometry(inFeat.geometry())
- if inGeom.isGeosEmpty() or not inGeom.isGeosValid():
- ProcessingLog.addToLog(ProcessingLog.LOG_WARNING, 'Feature {} has empty or invalid geometry. Skipping...'.format(inFeat.id()))
+ if inGeom.isGeosEmpty():
+ ProcessingLog.addToLog(ProcessingLog.LOG_WARNING, 'Feature {} has empty geometry. Skipping...'.format(inFeat.id()))
+ continue
+ if not inGeom.isGeosValid():
+ ProcessingLog.addToLog(ProcessingLog.LOG_WARNING, 'Feature {} has invalid geometry. Skipping...'.format(inFeat.id()))
continue
outGeom = inGeom.buffer(float(value), segments)
if first:
@@ -82,8 +85,11 @@ def buffering(progress, writer, distance, field, useField, layer, dissolve,
else:
value = distance
inGeom = QgsGeometry(inFeat.geometry())
- if inGeom.isGeosEmpty() or not inGeom.isGeosValid():
- ProcessingLog.addToLog(ProcessingLog.LOG_WARNING, 'Feature {} has empty or invalid geometry. Skipping...'.format(inFeat.id()))
+ if inGeom.isGeosEmpty():
+ ProcessingLog.addToLog(ProcessingLog.LOG_WARNING, 'Feature {} has empty geometry. Skipping...'.format(inFeat.id()))
+ continue
+ if not inGeom.isGeosValid():
+ ProcessingLog.addToLog(ProcessingLog.LOG_WARNING, 'Feature {} has invalid geometry. Skipping...'.format(inFeat.id()))
continue
outGeom = inGeom.buffer(float(value), segments)
diff --git a/python/plugins/processing/algs/qgis/Difference.py b/python/plugins/processing/algs/qgis/Difference.py
index d375c30..fafd79c 100644
--- a/python/plugins/processing/algs/qgis/Difference.py
+++ b/python/plugins/processing/algs/qgis/Difference.py
@@ -29,7 +29,7 @@ from qgis.core import QGis, QgsFeatureRequest, QgsFeature, QgsGeometry
from processing.core.ProcessingLog import ProcessingLog
from processing.core.GeoAlgorithm import GeoAlgorithm
from processing.core.GeoAlgorithmExecutionException import GeoAlgorithmExecutionException
-from processing.core.parameters import ParameterVector
+from processing.core.parameters import ParameterVector, ParameterBoolean
from processing.core.outputs import OutputVector
from processing.tools import dataobjects, vector
@@ -38,6 +38,7 @@ class Difference(GeoAlgorithm):
INPUT = 'INPUT'
OVERLAY = 'OVERLAY'
+ IGNORE_INVALID = 'IGNORE_INVALID'
OUTPUT = 'OUTPUT'
#==========================================================================
@@ -52,6 +53,8 @@ class Difference(GeoAlgorithm):
self.tr('Input layer'), [ParameterVector.VECTOR_TYPE_ANY]))
self.addParameter(ParameterVector(Difference.OVERLAY,
self.tr('Difference layer'), [ParameterVector.VECTOR_TYPE_ANY]))
+ self.addParameter(ParameterBoolean(Difference.IGNORE_INVALID,
+ self.tr('Ignore invalid input features'), False, True))
self.addOutput(OutputVector(Difference.OUTPUT, self.tr('Difference')))
def processAlgorithm(self, progress):
@@ -59,6 +62,7 @@ class Difference(GeoAlgorithm):
self.getParameterValue(Difference.INPUT))
layerB = dataobjects.getObjectFromUri(
self.getParameterValue(Difference.OVERLAY))
+ ignoreInvalid = self.getParameterValue(Difference.IGNORE_INVALID)
geomType = layerA.dataProvider().geometryType()
writer = self.getOutputFromName(
@@ -82,12 +86,16 @@ class Difference(GeoAlgorithm):
tmpGeom = QgsGeometry(inFeatB.geometry())
if diff_geom.intersects(tmpGeom):
diff_geom = QgsGeometry(diff_geom.difference(tmpGeom))
- if diff_geom.isGeosEmpty() or not diff_geom.isGeosValid():
- ProcessingLog.addToLog(ProcessingLog.LOG_ERROR,
- self.tr('GEOS geoprocessing error: One or '
- 'more input features have invalid '
- 'geometry.'))
- add = False
+ if diff_geom.isGeosEmpty():
+ ProcessingLog.addToLog(ProcessingLog.LOG_INFO,
+ self.tr('Feature with NULL geometry found.'))
+ if not diff_geom.isGeosValid():
+ if ignoreInvalid:
+ ProcessingLog.addToLog(ProcessingLog.LOG_ERROR,
+ self.tr('GEOS geoprocessing error: One or more input features have invalid geometry.'))
+ add = False
+ else:
+ raise GeoAlgorithmExecutionException(self.tr('Features with invalid geometries found. Please fix these errors or specify the "Ignore invalid input features" flag'))
break
if add:
diff --git a/python/plugins/processing/modeler/ModelerParametersDialog.py b/python/plugins/processing/modeler/ModelerParametersDialog.py
index 72f8336..dc22404 100644
--- a/python/plugins/processing/modeler/ModelerParametersDialog.py
+++ b/python/plugins/processing/modeler/ModelerParametersDialog.py
@@ -26,7 +26,22 @@ __copyright__ = '(C) 2012, Victor Olaya'
__revision__ = '$Format:%H$'
from PyQt4.QtCore import Qt, QUrl, QMetaObject
-from PyQt4.QtGui import QDialog, QDialogButtonBox, QLabel, QLineEdit, QFrame, QPushButton, QSizePolicy, QVBoxLayout, QHBoxLayout, QTabWidget, QWidget, QScrollArea, QComboBox, QTableWidgetItem, QMessageBox
+from PyQt4.QtGui import (QDialog,
+ QDialogButtonBox,
+ QLabel,
+ QLineEdit,
+ QFrame,
+ QPushButton,
+ QSizePolicy,
+ QVBoxLayout,
+ QHBoxLayout,
+ QTabWidget,
+ QWidget,
+ QScrollArea,
+ QComboBox,
+ QTableWidgetItem,
+ QMessageBox,
+ QTextBrowser)
from PyQt4.QtNetwork import QNetworkRequest, QNetworkReply
from qgis.core import QgsNetworkAccessManager
diff --git a/python/plugins/processing/tools/dataobjects.py b/python/plugins/processing/tools/dataobjects.py
index b00c57b..0c3640e 100644
--- a/python/plugins/processing/tools/dataobjects.py
+++ b/python/plugins/processing/tools/dataobjects.py
@@ -28,7 +28,8 @@ __revision__ = '$Format:%H$'
import os
import re
-from qgis.core import QGis, QgsProject, QgsVectorFileWriter, QgsMapLayer, QgsRasterLayer, QgsVectorLayer, QgsMapLayerRegistry, QgsCoordinateReferenceSystem
+from qgis.core import QGis, QgsProject, QgsVectorFileWriter, QgsMapLayer, QgsRasterLayer, \
+ QgsVectorLayer, QgsMapLayerRegistry, QgsCoordinateReferenceSystem
from qgis.gui import QgsSublayersDialog
from PyQt4.QtCore import QSettings
from qgis.utils import iface
@@ -289,18 +290,7 @@ def exportVectorLayer(layer, supported=None):
settings = QSettings()
systemEncoding = settings.value('/UI/encoding', 'System')
- filename = os.path.basename(unicode(layer.source()))
- idx = filename.rfind('.')
- if idx != -1:
- filename = filename[:idx]
-
- filename = unicode(layer.name())
- validChars = \
- 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789:'
- filename = ''.join(c for c in filename if c in validChars)
- if len(filename) == 0:
- filename = 'layer'
- output = getTempFilenameInTempFolder(filename + '.shp')
+ output = getTempFilename('shp')
provider = layer.dataProvider()
useSelection = ProcessingConfig.getSetting(ProcessingConfig.USE_SELECTED)
if useSelection and layer.selectedFeatureCount() != 0:
@@ -318,7 +308,7 @@ def exportVectorLayer(layer, supported=None):
unicode(layer.source()).decode('ascii')
except UnicodeEncodeError:
isASCII = False
- if not os.path.splitext()[1] in supported or not isASCII:
+ if not os.path.splitext(layer.source())[1].lower() in supported or not isASCII:
writer = QgsVectorFileWriter(
output, systemEncoding,
layer.pendingFields(), provider.geometryType(),
diff --git a/src/analysis/raster/qgsninecellfilter.cpp b/src/analysis/raster/qgsninecellfilter.cpp
index af31210..30ccce4 100644
--- a/src/analysis/raster/qgsninecellfilter.cpp
+++ b/src/analysis/raster/qgsninecellfilter.cpp
@@ -16,6 +16,7 @@
***************************************************************************/
#include "qgsninecellfilter.h"
+#include "qgslogger.h"
#include "cpl_string.h"
#include <QProgressDialog>
#include <QFile>
@@ -138,7 +139,10 @@ int QgsNineCellFilter::processRaster( QProgressDialog* p )
{
scanLine1[a] = mInputNodataValue;
}
- GDALRasterIO( rasterBand, GF_Read, 0, 0, xSize, 1, scanLine2, xSize, 1, GDT_Float32, 0, 0 );
+ if ( GDALRasterIO( rasterBand, GF_Read, 0, 0, xSize, 1, scanLine2, xSize, 1, GDT_Float32, 0, 0 ) != CE_None )
+ {
+ QgsDebugMsg( "Raster IO Error" );
+ }
}
else
{
@@ -158,7 +162,10 @@ int QgsNineCellFilter::processRaster( QProgressDialog* p )
}
else
{
- GDALRasterIO( rasterBand, GF_Read, 0, i + 1, xSize, 1, scanLine3, xSize, 1, GDT_Float32, 0, 0 );
+ if ( GDALRasterIO( rasterBand, GF_Read, 0, i + 1, xSize, 1, scanLine3, xSize, 1, GDT_Float32, 0, 0 ) != CE_None )
+ {
+ QgsDebugMsg( "Raster IO Error" );
+ }
}
for ( int j = 0; j < xSize; ++j )
@@ -180,7 +187,10 @@ int QgsNineCellFilter::processRaster( QProgressDialog* p )
}
}
- GDALRasterIO( outputRasterBand, GF_Write, 0, i, xSize, 1, resultLine, xSize, 1, GDT_Float32, 0, 0 );
+ if ( GDALRasterIO( outputRasterBand, GF_Write, 0, i, xSize, 1, resultLine, xSize, 1, GDT_Float32, 0, 0 ) != CE_None )
+ {
+ QgsDebugMsg( "Raster IO Error" );
+ }
}
if ( p )
diff --git a/src/analysis/raster/qgsrastercalculator.cpp b/src/analysis/raster/qgsrastercalculator.cpp
index 961e790..cd3f431 100644
--- a/src/analysis/raster/qgsrastercalculator.cpp
+++ b/src/analysis/raster/qgsrastercalculator.cpp
@@ -156,7 +156,7 @@ int QgsRasterCalculator::processCalculation( QProgressDialog* p )
//write scanline to the dataset
if ( GDALRasterIO( outputRasterBand, GF_Write, 0, i, mNumOutputColumns, 1, calcData, mNumOutputColumns, 1, GDT_Float32, 0, 0 ) != CE_None )
{
- qWarning( "RasterIO error!" );
+ QgsDebugMsg( "RasterIO error!" );
}
delete[] calcData;
diff --git a/src/analysis/raster/qgsrelief.cpp b/src/analysis/raster/qgsrelief.cpp
index 296f82d..974840a 100644
--- a/src/analysis/raster/qgsrelief.cpp
+++ b/src/analysis/raster/qgsrelief.cpp
@@ -15,6 +15,7 @@
* *
***************************************************************************/
+#include "qgslogger.h"
#include "qgsrelief.h"
#include "qgsaspectfilter.h"
#include "qgshillshadefilter.h"
@@ -202,7 +203,10 @@ int QgsRelief::processRaster( QProgressDialog* p )
{
scanLine1[a] = mInputNodataValue;
}
- GDALRasterIO( rasterBand, GF_Read, 0, 0, xSize, 1, scanLine2, xSize, 1, GDT_Float32, 0, 0 );
+ if ( GDALRasterIO( rasterBand, GF_Read, 0, 0, xSize, 1, scanLine2, xSize, 1, GDT_Float32, 0, 0 ) != CE_None )
+ {
+ QgsDebugMsg( "Raster IO Error" );
+ }
}
else
{
@@ -222,7 +226,10 @@ int QgsRelief::processRaster( QProgressDialog* p )
}
else
{
- GDALRasterIO( rasterBand, GF_Read, 0, i + 1, xSize, 1, scanLine3, xSize, 1, GDT_Float32, 0, 0 );
+ if ( GDALRasterIO( rasterBand, GF_Read, 0, i + 1, xSize, 1, scanLine3, xSize, 1, GDT_Float32, 0, 0 ) != CE_None )
+ {
+ QgsDebugMsg( "Raster IO Error" );
+ }
}
for ( int j = 0; j < xSize; ++j )
@@ -254,9 +261,18 @@ int QgsRelief::processRaster( QProgressDialog* p )
}
}
- GDALRasterIO( outputRedBand, GF_Write, 0, i, xSize, 1, resultRedLine, xSize, 1, GDT_Byte, 0, 0 );
- GDALRasterIO( outputGreenBand, GF_Write, 0, i, xSize, 1, resultGreenLine, xSize, 1, GDT_Byte, 0, 0 );
- GDALRasterIO( outputBlueBand, GF_Write, 0, i, xSize, 1, resultBlueLine, xSize, 1, GDT_Byte, 0, 0 );
+ if ( GDALRasterIO( outputRedBand, GF_Write, 0, i, xSize, 1, resultRedLine, xSize, 1, GDT_Byte, 0, 0 ) != CE_None )
+ {
+ QgsDebugMsg( "Raster IO Error" );
+ }
+ if ( GDALRasterIO( outputGreenBand, GF_Write, 0, i, xSize, 1, resultGreenLine, xSize, 1, GDT_Byte, 0, 0 ) != CE_None )
+ {
+ QgsDebugMsg( "Raster IO Error" );
+ }
+ if ( GDALRasterIO( outputBlueBand, GF_Write, 0, i, xSize, 1, resultBlueLine, xSize, 1, GDT_Byte, 0, 0 ) != CE_None )
+ {
+ QgsDebugMsg( "Raster IO Error" );
+ }
}
if ( p )
@@ -531,9 +547,13 @@ bool QgsRelief::exportFrequencyDistributionToCsv( const QString& file )
for ( int i = 0; i < nCellsY; ++i )
{
- GDALRasterIO( elevationBand, GF_Read, 0, i, nCellsX, 1,
- scanLine, nCellsX, 1, GDT_Float32,
- 0, 0 );
+ if ( GDALRasterIO( elevationBand, GF_Read, 0, i, nCellsX, 1,
+ scanLine, nCellsX, 1, GDT_Float32,
+ 0, 0 ) != CE_None )
+ {
+ QgsDebugMsg( "Raster IO Error" );
+ }
+
for ( int j = 0; j < nCellsX; ++j )
{
elevationClass = frequencyClassForElevation( scanLine[j], minMax[0], frequencyClassRange );
@@ -614,9 +634,12 @@ QList< QgsRelief::ReliefColor > QgsRelief::calculateOptimizedReliefClasses()
for ( int i = 0; i < nCellsY; ++i )
{
- GDALRasterIO( elevationBand, GF_Read, 0, i, nCellsX, 1,
- scanLine, nCellsX, 1, GDT_Float32,
- 0, 0 );
+ if ( GDALRasterIO( elevationBand, GF_Read, 0, i, nCellsX, 1,
+ scanLine, nCellsX, 1, GDT_Float32,
+ 0, 0 ) != CE_None )
+ {
+ QgsDebugMsg( "Raster IO Error" );
+ }
for ( int j = 0; j < nCellsX; ++j )
{
elevationClass = frequencyClassForElevation( scanLine[j], minMax[0], frequencyClassRange );
diff --git a/src/analysis/vector/qgszonalstatistics.cpp b/src/analysis/vector/qgszonalstatistics.cpp
index ec93f14..289a156 100644
--- a/src/analysis/vector/qgszonalstatistics.cpp
+++ b/src/analysis/vector/qgszonalstatistics.cpp
@@ -482,7 +482,11 @@ void QgsZonalStatistics::statisticsFromPreciseIntersection( void* band, const Qg
double currentX = rasterBBox.xMinimum() + cellSizeX / 2.0 + pixelOffsetX * cellSizeX;
for ( int col = 0; col < nCellsX; ++col )
{
- GDALRasterIO( band, GF_Read, pixelOffsetX + col, pixelOffsetY + row, nCellsX, 1, pixelData, 1, 1, GDT_Float32, 0, 0 );
+ if ( GDALRasterIO( band, GF_Read, pixelOffsetX + col, pixelOffsetY + row, nCellsX, 1, pixelData, 1, 1, GDT_Float32, 0, 0 ) != CE_None )
+ {
+ QgsDebugMsg( "Raster IO Error" );
+ }
+
if ( !validPixel( *pixelData ) )
continue;
diff --git a/src/app/composer/qgsatlascompositionwidget.cpp b/src/app/composer/qgsatlascompositionwidget.cpp
index d0ea077..2c6c091 100644
--- a/src/app/composer/qgsatlascompositionwidget.cpp
+++ b/src/app/composer/qgsatlascompositionwidget.cpp
@@ -271,8 +271,9 @@ void QgsAtlasCompositionWidget::on_mAtlasFeatureFilterCheckBox_stateChanged( int
updateAtlasFeatures();
}
-void QgsAtlasCompositionWidget::pageNameExpressionChanged( const QString& expression, bool valid )
+void QgsAtlasCompositionWidget::pageNameExpressionChanged( const QString&, bool valid )
{
+ QString expression = mPageNameWidget->asExpression();
QgsAtlasComposition* atlasMap = &mComposition->atlasComposition();
if ( !atlasMap || ( !valid && !expression.isEmpty() ) )
{
diff --git a/src/app/composer/qgscomposer.cpp b/src/app/composer/qgscomposer.cpp
index 33c76be..e6618d3 100644
--- a/src/app/composer/qgscomposer.cpp
+++ b/src/app/composer/qgscomposer.cpp
@@ -877,6 +877,30 @@ void QgsComposer::setTitle( const QString& title )
}
}
+bool QgsComposer::loadFromTemplate( const QDomDocument& templateDoc, bool clearExisting )
+{
+ // provide feedback, since composer will be hidden when loading template (much faster)
+ QScopedPointer< QDialog > dlg( new QgsBusyIndicatorDialog( tr( "Loading template into composer..." ), this ) );
+ dlg->setStyleSheet( mQgis->styleSheet() );
+ dlg->show();
+
+ setUpdatesEnabled( false );
+ bool result = mComposition->loadFromTemplate( templateDoc, nullptr, false, clearExisting );
+ setUpdatesEnabled( true );
+
+ dlg->close();
+
+ if ( result )
+ {
+ // update composition widget
+ QgsCompositionWidget* oldCompositionWidget = qobject_cast<QgsCompositionWidget *>( mGeneralDock->widget() );
+ delete oldCompositionWidget;
+ createCompositionWidget();
+ }
+
+ return result;
+}
+
void QgsComposer::updateStatusCursorPos( QPointF cursorPosition )
{
if ( !mComposition )
@@ -3040,11 +3064,9 @@ void QgsComposer::on_mActionSaveAsTemplate_triggered()
void QgsComposer::on_mActionLoadFromTemplate_triggered()
{
- loadTemplate( false );
-}
+ if ( !mComposition )
+ return;
-void QgsComposer::loadTemplate( const bool newComposer )
-{
QSettings settings;
QString openFileDir = settings.value( "UI/lastComposerTemplateDir", QDir::homePath() ).toString();
QString openFileString = QFileDialog::getOpenFileName( nullptr, tr( "Load template" ), openFileDir, "*.qpt" );
@@ -3064,48 +3086,10 @@ void QgsComposer::loadTemplate( const bool newComposer )
return;
}
- QgsComposer* c = nullptr;
- QgsComposition* comp = nullptr;
-
- if ( newComposer )
+ QDomDocument templateDoc;
+ if ( templateDoc.setContent( &templateFile ) )
{
- QString newTitle;
- if ( !mQgis->uniqueComposerTitle( this, newTitle, true ) )
- {
- return;
- }
- c = mQgis->createNewComposer( newTitle );
- if ( !c )
- {
- QMessageBox::warning( this, tr( "Composer error" ), tr( "Error, could not create new composer" ) );
- return;
- }
- comp = c->composition();
- }
- else
- {
- c = this;
- comp = mComposition;
- }
-
- if ( comp )
- {
- QDomDocument templateDoc;
- if ( templateDoc.setContent( &templateFile ) )
- {
- // provide feedback, since composer will be hidden when loading template (much faster)
- QDialog* dlg = new QgsBusyIndicatorDialog( tr( "Loading template into composer..." ) );
- dlg->setStyleSheet( mQgis->styleSheet() );
- dlg->show();
-
- c->setUpdatesEnabled( false );
- comp->loadFromTemplate( templateDoc, nullptr, false, newComposer );
- c->setUpdatesEnabled( true );
-
- dlg->close();
- delete dlg;
- dlg = nullptr;
- }
+ loadFromTemplate( templateDoc, false );
}
}
diff --git a/src/app/composer/qgscomposer.h b/src/app/composer/qgscomposer.h
index aa9ad48..4eb573a 100644
--- a/src/app/composer/qgscomposer.h
+++ b/src/app/composer/qgscomposer.h
@@ -99,9 +99,12 @@ class QgsComposer: public QMainWindow, private Ui::QgsComposerBase
const QString& title() const {return mTitle;}
void setTitle( const QString& title );
- //! Load template into current or blank composer
- //! @param newComposer whether to create a new composer first
- void loadTemplate( const bool newComposer );
+ /** Loads the contents of a template document into the composer's composition.
+ * @param templateDoc template document to load
+ * @param clearExisting set to true to remove all existing composition settings and items before loading template
+ * @returns true if template load was successful
+ */
+ bool loadFromTemplate( const QDomDocument& templateDoc, bool clearExisting );
protected:
//! Move event
diff --git a/src/app/composer/qgscomposeritemwidget.cpp b/src/app/composer/qgscomposeritemwidget.cpp
index f0f8a5c..280edc3 100644
--- a/src/app/composer/qgscomposeritemwidget.cpp
+++ b/src/app/composer/qgscomposeritemwidget.cpp
@@ -23,6 +23,7 @@
#include "qgspoint.h"
#include "qgsdatadefinedbutton.h"
#include "qgsexpressioncontext.h"
+#include "qgsproject.h"
#include <QColorDialog>
#include <QPen>
@@ -109,6 +110,16 @@ QgsVectorLayer* QgsComposerItemBaseWidget::atlasCoverageLayer() const
//QgsComposerItemWidget
+void QgsComposerItemWidget::updateVariables()
+{
+ QgsExpressionContext* context = mItem->createExpressionContext();
+ mVariableEditor->setContext( context );
+ int editableIndex = context->indexOfScope( tr( "Composer Item" ) );
+ if ( editableIndex >= 0 )
+ mVariableEditor->setEditableScopeIndex( editableIndex );
+ delete context;
+}
+
QgsComposerItemWidget::QgsComposerItemWidget( QWidget* parent, QgsComposerItem* item )
: QgsComposerItemBaseWidget( parent, item )
, mItem( item )
@@ -141,12 +152,17 @@ QgsComposerItemWidget::QgsComposerItemWidget( QWidget* parent, QgsComposerItem*
connect( mTransparencySlider, SIGNAL( valueChanged( int ) ), mTransparencySpnBx, SLOT( setValue( int ) ) );
- QgsExpressionContext* context = mItem->createExpressionContext();
- mVariableEditor->setContext( context );
- mVariableEditor->setEditableScopeIndex( context->scopeCount() - 1 );
- delete context;
-
+ updateVariables();
connect( mVariableEditor, SIGNAL( scopeChanged() ), this, SLOT( variablesChanged() ) );
+ // listen out for variable edits
+ QgsApplication* app = qobject_cast<QgsApplication*>( QgsApplication::instance() );
+ if ( app )
+ {
+ connect( app, SIGNAL( settingsChanged() ), this, SLOT( updateVariables() ) );
+ }
+ connect( QgsProject::instance(), SIGNAL( variablesChanged() ), this, SLOT( updateVariables() ) );
+ if ( mItem->composition() )
+ connect( mItem->composition(), SIGNAL( variablesChanged() ), this, SLOT( updateVariables() ) );
//connect atlas signals to data defined buttons
QgsAtlasComposition* atlas = atlasComposition();
diff --git a/src/app/composer/qgscomposeritemwidget.h b/src/app/composer/qgscomposeritemwidget.h
index f5e884c..10ea98b 100644
--- a/src/app/composer/qgscomposeritemwidget.h
+++ b/src/app/composer/qgscomposeritemwidget.h
@@ -142,8 +142,7 @@ class QgsComposerItemWidget: public QgsComposerItemBaseWidget, private Ui::QgsCo
private slots:
void variablesChanged();
-
-
+ void updateVariables();
};
#endif //QGSCOMPOSERITEMWIDGET_H
diff --git a/src/app/composer/qgscomposerlegendwidget.cpp b/src/app/composer/qgscomposerlegendwidget.cpp
index f3de5bb..b48f23d 100644
--- a/src/app/composer/qgscomposerlegendwidget.cpp
+++ b/src/app/composer/qgscomposerlegendwidget.cpp
@@ -134,6 +134,8 @@ void QgsComposerLegendWidget::setGuiElements()
mCheckBoxAutoUpdate->setChecked( mLegend->autoUpdateModel() );
refreshMapComboBox();
+ mCheckboxResizeContents->setChecked( mLegend->resizeToContents() );
+
const QgsComposerMap* map = mLegend->composerMap();
if ( map )
{
@@ -590,6 +592,21 @@ void QgsComposerLegendWidget::on_mMapComboBox_currentIndexChanged( int index )
}
}
+void QgsComposerLegendWidget::on_mCheckboxResizeContents_toggled( bool checked )
+{
+ if ( !mLegend )
+ {
+ return;
+ }
+
+ mLegend->beginCommand( tr( "Legend resize to contents" ) );
+ mLegend->setResizeToContents( checked );
+ if ( checked )
+ mLegend->adjustBoxSize();
+ mLegend->updateItem();
+ mLegend->endCommand();
+}
+
void QgsComposerLegendWidget::on_mRasterBorderGroupBox_toggled( bool state )
{
if ( !mLegend )
@@ -902,6 +919,10 @@ void QgsComposerLegendWidget::blockAllSignals( bool b )
mRasterBorderGroupBox->blockSignals( b );
mRasterBorderColorButton->blockSignals( b );
mRasterBorderWidthSpinBox->blockSignals( b );
+ mWmsLegendWidthSpinBox->blockSignals( b );
+ mWmsLegendHeightSpinBox->blockSignals( b );
+ mCheckboxResizeContents->blockSignals( b );
+ mTitleSpaceBottomSpinBox->blockSignals( b );
}
void QgsComposerLegendWidget::refreshMapComboBox()
diff --git a/src/app/composer/qgscomposerlegendwidget.h b/src/app/composer/qgscomposerlegendwidget.h
index 738951e..9fba67c 100644
--- a/src/app/composer/qgscomposerlegendwidget.h
+++ b/src/app/composer/qgscomposerlegendwidget.h
@@ -68,6 +68,7 @@ class QgsComposerLegendWidget: public QgsComposerItemBaseWidget, private Ui::Qgs
void on_mColumnSpaceSpinBox_valueChanged( double d );
void on_mCheckBoxAutoUpdate_stateChanged( int state );
void on_mMapComboBox_currentIndexChanged( int index );
+ void on_mCheckboxResizeContents_toggled( bool checked );
void on_mRasterBorderGroupBox_toggled( bool state );
void on_mRasterBorderWidthSpinBox_valueChanged( double d );
diff --git a/src/app/composer/qgscomposermanager.cpp b/src/app/composer/qgscomposermanager.cpp
index d074d9f..81fdf30 100644
--- a/src/app/composer/qgscomposermanager.cpp
+++ b/src/app/composer/qgscomposermanager.cpp
@@ -289,19 +289,8 @@ void QgsComposerManager::on_mAddButton_clicked()
QDomDocument templateDoc;
if ( templateDoc.setContent( &templateFile, false ) )
{
- // provide feedback, since composer will be hidden when loading template (much faster)
- // (not needed for empty composer)
- QDialog* dlg = new QgsBusyIndicatorDialog( tr( "Loading template into composer..." ) );
- dlg->setStyleSheet( QgisApp::instance()->styleSheet() );
- dlg->show();
-
- newComposer->hide();
- loadedOK = newComposer->composition()->loadFromTemplate( templateDoc, nullptr, false );
+ loadedOK = newComposer->loadFromTemplate( templateDoc, true );
newComposer->activate();
-
- dlg->close();
- delete dlg;
- dlg = nullptr;
}
}
diff --git a/src/app/composer/qgscompositionwidget.cpp b/src/app/composer/qgscompositionwidget.cpp
index c6716f0..ed92569 100644
--- a/src/app/composer/qgscompositionwidget.cpp
+++ b/src/app/composer/qgscompositionwidget.cpp
@@ -23,6 +23,7 @@
#include "qgssymbolv2selectordialog.h"
#include "qgssymbollayerv2utils.h"
#include "qgsexpressioncontext.h"
+#include "qgsproject.h"
#include <QColorDialog>
#include <QWidget>
#include <QPrinter> //for screen resolution
@@ -45,13 +46,15 @@ QgsCompositionWidget::QgsCompositionWidget( QWidget* parent, QgsComposition* c )
//read with/height from composition and find suitable entries to display
displayCompositionWidthHeight();
- mVariableEditor->context()->appendScope( QgsExpressionContextUtils::globalScope() );
- mVariableEditor->context()->appendScope( QgsExpressionContextUtils::projectScope() );
- mVariableEditor->context()->appendScope( QgsExpressionContextUtils::compositionScope( mComposition ) );
- mVariableEditor->reloadContext();
- mVariableEditor->setEditableScopeIndex( 2 );
-
+ updateVariables();
connect( mVariableEditor, SIGNAL( scopeChanged() ), this, SLOT( variablesChanged() ) );
+ // listen out for variable edits
+ QgsApplication* app = qobject_cast<QgsApplication*>( QgsApplication::instance() );
+ if ( app )
+ {
+ connect( app, SIGNAL( settingsChanged() ), this, SLOT( updateVariables() ) );
+ }
+ connect( QgsProject::instance(), SIGNAL( variablesChanged() ), this, SLOT( updateVariables() ) );
if ( mComposition )
{
@@ -221,6 +224,16 @@ void QgsCompositionWidget::resizeMarginsChanged()
mLeftMarginSpinBox->value() );
}
+void QgsCompositionWidget::updateVariables()
+{
+ QgsExpressionContext context;
+ context << QgsExpressionContextUtils::globalScope()
+ << QgsExpressionContextUtils::projectScope()
+ << QgsExpressionContextUtils::compositionScope( mComposition );
+ mVariableEditor->setContext( &context );
+ mVariableEditor->setEditableScopeIndex( 2 );
+}
+
void QgsCompositionWidget::setDataDefinedProperty( const QgsDataDefinedButton* ddBtn, QgsComposerObject::DataDefinedProperty property )
{
if ( !mComposition )
diff --git a/src/app/composer/qgscompositionwidget.h b/src/app/composer/qgscompositionwidget.h
index 5283155..56ab79f 100644
--- a/src/app/composer/qgscompositionwidget.h
+++ b/src/app/composer/qgscompositionwidget.h
@@ -88,6 +88,8 @@ class QgsCompositionWidget: public QWidget, private Ui::QgsCompositionWidgetBase
void resizeMarginsChanged();
+ void updateVariables();
+
private:
QgsComposition* mComposition;
QMap<QString, QgsCompositionPaper> mPaperMap;
diff --git a/src/app/qgisapp.cpp b/src/app/qgisapp.cpp
index 8b71c47..cc174fa 100644
--- a/src/app/qgisapp.cpp
+++ b/src/app/qgisapp.cpp
@@ -6137,8 +6137,7 @@ QgsComposer* QgisApp::duplicateComposer( QgsComposer* currentComposer, QString t
// hiding composer until template is loaded is much faster, provide feedback to user
newComposer->hide();
- QApplication::setOverrideCursor( Qt::BusyCursor );
- if ( !newComposer->composition()->loadFromTemplate( currentDoc, nullptr, false ) )
+ if ( !newComposer->loadFromTemplate( currentDoc, true ) )
{
deleteComposer( newComposer );
newComposer = nullptr;
@@ -6146,7 +6145,6 @@ QgsComposer* QgisApp::duplicateComposer( QgsComposer* currentComposer, QString t
return newComposer;
}
newComposer->activate();
- QApplication::restoreOverrideCursor();
return newComposer;
}
@@ -10880,8 +10878,6 @@ void QgisApp::namSetup()
{
QgsNetworkAccessManager *nam = QgsNetworkAccessManager::instance();
- namUpdate();
-
connect( nam, SIGNAL( authenticationRequired( QNetworkReply *, QAuthenticator * ) ),
this, SLOT( namAuthenticationRequired( QNetworkReply *, QAuthenticator * ) ) );
diff --git a/src/app/qgsannotationwidget.cpp b/src/app/qgsannotationwidget.cpp
index a1dce25..dacff37 100644
--- a/src/app/qgsannotationwidget.cpp
+++ b/src/app/qgsannotationwidget.cpp
@@ -54,6 +54,8 @@ QgsAnnotationWidget::QgsAnnotationWidget( QgsAnnotationItem* item, QWidget * par
mBackgroundColorButton->setNoColorString( tr( "Transparent" ) );
mBackgroundColorButton->setShowNoColor( true );
+ connect( mBackgroundColorButton, SIGNAL( colorChanged( QColor ) ), this, SIGNAL( backgroundColorChanged( QColor ) ) );
+
const QgsMarkerSymbolV2* symbol = mItem->markerSymbol();
if ( symbol )
{
@@ -112,16 +114,6 @@ void QgsAnnotationWidget::on_mMapMarkerButton_clicked()
}
}
-void QgsAnnotationWidget::on_mFrameColorButton_colorChanged( const QColor &color )
-{
- if ( !mItem )
- {
- return;
- }
-
- mItem->setFrameColor( color );
-}
-
void QgsAnnotationWidget::updateCenterIcon()
{
if ( !mMarkerSymbol )
@@ -132,13 +124,3 @@ void QgsAnnotationWidget::updateCenterIcon()
mMapMarkerButton->setIcon( icon );
}
-void QgsAnnotationWidget::on_mBackgroundColorButton_colorChanged( const QColor &color )
-{
- if ( !mItem )
- {
- return;
- }
-
- mItem->setFrameBackgroundColor( color );
-}
-
diff --git a/src/app/qgsannotationwidget.h b/src/app/qgsannotationwidget.h
index 1c0855d..49d530f 100644
--- a/src/app/qgsannotationwidget.h
+++ b/src/app/qgsannotationwidget.h
@@ -34,10 +34,13 @@ class APP_EXPORT QgsAnnotationWidget: public QWidget, private Ui::QgsAnnotationW
void apply();
+ signals:
+
+ //! Emitted when the background color of the annotation is changed
+ void backgroundColorChanged( const QColor& color );
+
private slots:
void on_mMapMarkerButton_clicked();
- void on_mFrameColorButton_colorChanged( const QColor &color );
- void on_mBackgroundColorButton_colorChanged( const QColor &color );
private:
QgsAnnotationItem* mItem;
diff --git a/src/app/qgsattributetabledialog.cpp b/src/app/qgsattributetabledialog.cpp
index fd69f58..8a1684b 100644
--- a/src/app/qgsattributetabledialog.cpp
+++ b/src/app/qgsattributetabledialog.cpp
@@ -156,7 +156,6 @@ QgsAttributeTableDialog::QgsAttributeTableDialog( QgsVectorLayer *theLayer, QWid
if ( myDockFlag )
{
mDock = new QgsAttributeTableDock( tr( "%1 (%n Feature(s))", "feature count", mMainView->featureCount() ).arg( mLayer->name() ), QgisApp::instance() );
- mDock->setAllowedAreas( Qt::BottomDockWidgetArea | Qt::TopDockWidgetArea );
mDock->setWidget( this );
connect( this, SIGNAL( destroyed() ), mDock, SLOT( close() ) );
QgisApp::instance()->addDockWidget( Qt::BottomDockWidgetArea, mDock );
diff --git a/src/app/qgsformannotationdialog.cpp b/src/app/qgsformannotationdialog.cpp
index bc14b4c..04c99e7 100644
--- a/src/app/qgsformannotationdialog.cpp
+++ b/src/app/qgsformannotationdialog.cpp
@@ -24,7 +24,6 @@ QgsFormAnnotationDialog::QgsFormAnnotationDialog( QgsFormAnnotationItem* item, Q
{
setupUi( this );
mEmbeddedWidget = new QgsAnnotationWidget( mItem );
- mEmbeddedWidget->show();
mStackedWidget->addWidget( mEmbeddedWidget );
mStackedWidget->setCurrentWidget( mEmbeddedWidget );
diff --git a/src/app/qgshtmlannotationdialog.cpp b/src/app/qgshtmlannotationdialog.cpp
index fbdd2f4..06168fc 100644
--- a/src/app/qgshtmlannotationdialog.cpp
+++ b/src/app/qgshtmlannotationdialog.cpp
@@ -25,7 +25,6 @@ QgsHtmlAnnotationDialog::QgsHtmlAnnotationDialog( QgsHtmlAnnotationItem* item, Q
setupUi( this );
setWindowTitle( tr( "HTML annotation" ) );
mEmbeddedWidget = new QgsAnnotationWidget( mItem );
- mEmbeddedWidget->show();
mStackedWidget->addWidget( mEmbeddedWidget );
mStackedWidget->setCurrentWidget( mEmbeddedWidget );
diff --git a/src/app/qgsoptions.cpp b/src/app/qgsoptions.cpp
index 4afba0b..6aed1bf 100644
--- a/src/app/qgsoptions.cpp
+++ b/src/app/qgsoptions.cpp
@@ -311,16 +311,12 @@ QgsOptions::QgsOptions( QWidget *parent, Qt::WindowFlags fl ) :
}
// cache settings
- QNetworkDiskCache *cache = qobject_cast<QNetworkDiskCache*>( QgsNetworkAccessManager::instance()->cache() );
- if ( cache )
- {
- mCacheDirectory->setText( cache->cacheDirectory() );
- mCacheSize->setMinimum( 0 );
- mCacheSize->setMaximum( std::numeric_limits<int>::max() );
- mCacheSize->setSingleStep( 1024 );
- QgsDebugMsg( QString( "set cacheSize: %1" ).arg( cache->maximumCacheSize() ) );
- mCacheSize->setValue( cache->maximumCacheSize() / 1024 );
- }
+ mCacheDirectory->setText( mSettings->value( "cache/directory" ).toString() );
+ mCacheDirectory->setPlaceholderText( QDir( QgsApplication::qgisSettingsDirPath() ).canonicalPath() + QDir::separator() + "cache" );
+ mCacheSize->setMinimum( 0 );
+ mCacheSize->setMaximum( std::numeric_limits<int>::max() );
+ mCacheSize->setSingleStep( 1024 );
+ mCacheSize->setValue( mSettings->value( "cache/size" ).toInt() / 1024 );
//wms search server
leWmsSearch->setText( mSettings->value( "/qgis/WMSSearchUrl", "http://geopole.org/wms/search?search=%1&type=rss" ).toString() );
@@ -1090,7 +1086,11 @@ void QgsOptions::saveOptions()
mSettings->setValue( "proxy/proxyPassword", leProxyPassword->text() );
mSettings->setValue( "proxy/proxyType", mProxyTypeComboBox->currentText() );
- mSettings->setValue( "cache/directory", mCacheDirectory->text() );
+ if ( !mCacheDirectory->text().isEmpty() )
+ mSettings->setValue( "cache/directory", mCacheDirectory->text() );
+ else
+ mSettings->remove( "cache/directory" );
+
mSettings->setValue( "cache/size", QVariant::fromValue( mCacheSize->value()*1024L ) );
//url to exclude from proxys
@@ -1418,6 +1418,12 @@ void QgsOptions::saveOptions()
}
saveDefaultDatumTransformations();
+
+ QgsApplication* app = qobject_cast<QgsApplication*>( QgsApplication::instance() );
+ if ( app )
+ {
+ app->emitSettingsChanged();
+ }
}
void QgsOptions::rejectOptions()
diff --git a/src/app/qgsprojectproperties.cpp b/src/app/qgsprojectproperties.cpp
index 044506f..c58308d 100644
--- a/src/app/qgsprojectproperties.cpp
+++ b/src/app/qgsprojectproperties.cpp
@@ -139,7 +139,7 @@ QgsProjectProperties::QgsProjectProperties( QgsMapCanvas* mapCanvas, QWidget *pa
// Properties stored in QgsProject
title( QgsProject::instance()->title() );
- projectFileName->setText( QgsProject::instance()->fileName() );
+ mProjectFileLineEdit->setText( QgsProject::instance()->fileName() );
// get the manner in which the number of decimal places in the mouse
// position display is set (manual or automatic)
@@ -1151,6 +1151,7 @@ void QgsProjectProperties::apply()
//save variables
QgsExpressionContextUtils::setProjectVariables( mVariableEditor->variablesInActiveScope() );
+ QgsProject::instance()->emitVariablesChanged();
emit refresh();
}
diff --git a/src/app/qgssvgannotationdialog.cpp b/src/app/qgssvgannotationdialog.cpp
index 93ba43c..453421f 100644
--- a/src/app/qgssvgannotationdialog.cpp
+++ b/src/app/qgssvgannotationdialog.cpp
@@ -28,7 +28,6 @@ QgsSvgAnnotationDialog::QgsSvgAnnotationDialog( QgsSvgAnnotationItem* item, QWid
setupUi( this );
setWindowTitle( tr( "SVG annotation" ) );
mEmbeddedWidget = new QgsAnnotationWidget( mItem );
- mEmbeddedWidget->show();
mStackedWidget->addWidget( mEmbeddedWidget );
mStackedWidget->setCurrentWidget( mEmbeddedWidget );
diff --git a/src/app/qgstextannotationdialog.cpp b/src/app/qgstextannotationdialog.cpp
index 1b6ed20..ef32a60 100644
--- a/src/app/qgstextannotationdialog.cpp
+++ b/src/app/qgstextannotationdialog.cpp
@@ -25,9 +25,10 @@ QgsTextAnnotationDialog::QgsTextAnnotationDialog( QgsTextAnnotationItem* item, Q
{
setupUi( this );
mEmbeddedWidget = new QgsAnnotationWidget( mItem );
- mEmbeddedWidget->show();
mStackedWidget->addWidget( mEmbeddedWidget );
mStackedWidget->setCurrentWidget( mEmbeddedWidget );
+ connect( mEmbeddedWidget, SIGNAL( backgroundColorChanged( QColor ) ), this, SLOT( backgroundColorChanged( QColor ) ) );
+ mTextEdit->setAttribute( Qt::WA_TranslucentBackground );
if ( mItem )
{
mTextDocument = mItem->document();
@@ -57,6 +58,18 @@ QgsTextAnnotationDialog::~QgsTextAnnotationDialog()
delete mTextDocument;
}
+void QgsTextAnnotationDialog::showEvent( QShowEvent* )
+{
+ backgroundColorChanged( mItem ? mItem->frameBackgroundColor() : Qt::white );
+}
+
+void QgsTextAnnotationDialog::backgroundColorChanged( const QColor& color )
+{
+ QPalette p = mTextEdit->viewport()->palette();
+ p.setColor( QPalette::Base, color );
+ mTextEdit->viewport()->setPalette( p );
+}
+
void QgsTextAnnotationDialog::applyTextToItem()
{
if ( mItem && mTextDocument )
diff --git a/src/app/qgstextannotationdialog.h b/src/app/qgstextannotationdialog.h
index 26c8807..3634f42 100644
--- a/src/app/qgstextannotationdialog.h
+++ b/src/app/qgstextannotationdialog.h
@@ -30,6 +30,10 @@ class APP_EXPORT QgsTextAnnotationDialog: public QDialog, private Ui::QgsTextAnn
QgsTextAnnotationDialog( QgsTextAnnotationItem* item, QWidget * parent = nullptr, Qt::WindowFlags f = nullptr );
~QgsTextAnnotationDialog();
+ protected:
+
+ virtual void showEvent( QShowEvent * event ) override;
+
private:
QgsTextAnnotationItem* mItem;
/** Text document (a clone of the annotation items document)*/
@@ -44,6 +48,7 @@ class APP_EXPORT QgsTextAnnotationDialog: public QDialog, private Ui::QgsTextAnn
void on_mFontColorButton_colorChanged( const QColor& color );
void setCurrentFontPropertiesToGui();
void deleteItem();
+ void backgroundColorChanged( const QColor& color );
};
#endif // QGSTEXTANNOTATIONDIALOG_H
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index bbbdf8d..468d698 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -581,6 +581,7 @@ SET(QGIS_CORE_HDRS
../plugins/qgisplugin.h
qgis.h
+ qgsannotation.h
qgsattributeaction.h
qgscachedfeatureiterator.h
qgscacheindex.h
diff --git a/src/core/composer/qgsatlascomposition.cpp b/src/core/composer/qgsatlascomposition.cpp
index fbdfeea..d74580f 100644
--- a/src/core/composer/qgsatlascomposition.cpp
+++ b/src/core/composer/qgsatlascomposition.cpp
@@ -216,7 +216,10 @@ int QgsAtlasComposition::updateFeatures()
{
nameExpression.reset( nullptr );
}
- nameExpression->prepare( &expressionContext );
+ else
+ {
+ nameExpression->prepare( &expressionContext );
+ }
}
// We cannot use nextFeature() directly since the feature pointer is rewinded by the rendering process
diff --git a/src/core/composer/qgscomposerlegend.cpp b/src/core/composer/qgscomposerlegend.cpp
index d51b5aa..f14d939 100644
--- a/src/core/composer/qgscomposerlegend.cpp
+++ b/src/core/composer/qgscomposerlegend.cpp
@@ -42,11 +42,12 @@ QgsComposerLegend::QgsComposerLegend( QgsComposition* composition )
, mFilterOutAtlas( false )
, mFilterAskedForUpdate( false )
, mInAtlas( false )
+ , mInitialMapScaleCalculated( false )
+ , mForceResize( false )
+ , mSizeToContents( true )
{
mLegendModel2 = new QgsLegendModelV2( QgsProject::instance()->layerTreeRoot() );
- adjustBoxSize();
-
connect( &mLegendModel, SIGNAL( layersChanged() ), this, SLOT( synchronizeWithModel() ) );
connect( &composition->atlasComposition(), SIGNAL( renderEnded() ), this, SLOT( onAtlasEnded() ) );
@@ -67,6 +68,9 @@ QgsComposerLegend::QgsComposerLegend()
, mFilterOutAtlas( false )
, mFilterAskedForUpdate( false )
, mInAtlas( false )
+ , mInitialMapScaleCalculated( false )
+ , mForceResize( false )
+ , mSizeToContents( true )
{
}
@@ -115,6 +119,35 @@ void QgsComposerLegend::paint( QPainter* painter, const QStyleOptionGraphicsItem
ms.setOutputDpi( dpi );
mSettings.setMapScale( ms.scale() );
}
+ mInitialMapScaleCalculated = true;
+
+ QgsLegendRenderer legendRenderer( mLegendModel2, mSettings );
+ legendRenderer.setLegendSize( mForceResize && mSizeToContents ? QSize() : rect().size() );
+
+ //adjust box if width or height is too small
+ if ( mSizeToContents )
+ {
+ QSizeF size = legendRenderer.minimumSize();
+ if ( mForceResize )
+ {
+ mForceResize = false;
+ //set new rect, respecting position mode and data defined size/position
+ QRectF targetRect = QRectF( pos().x(), pos().y(), size.width(), size.height() );
+ setSceneRect( evalItemRect( targetRect, true ) );
+ }
+ else if ( size.height() > rect().height() || size.width() > rect().width() )
+ {
+ //need to resize box
+ QRectF targetRect = QRectF( pos().x(), pos().y(), rect().width(), rect().height() );
+ if ( size.height() > targetRect.height() )
+ targetRect.setHeight( size.height() );
+ if ( size.width() > rect().width() )
+ targetRect.setWidth( size.width() );
+
+ //set new rect, respecting position mode and data defined size/position
+ setSceneRect( evalItemRect( targetRect, true ) );
+ }
+ }
drawBackground( painter );
painter->save();
@@ -122,22 +155,11 @@ void QgsComposerLegend::paint( QPainter* painter, const QStyleOptionGraphicsItem
painter->setRenderHint( QPainter::Antialiasing, true );
painter->setPen( QPen( QColor( 0, 0, 0 ) ) );
- QgsLegendRenderer legendRenderer( mLegendModel2, mSettings );
- legendRenderer.setLegendSize( rect().size() );
-
- //adjust box if width or height is too small
- QSizeF size = legendRenderer.minimumSize();
- if ( size.height() > rect().height() || size.width() > rect().width() )
+ if ( !mSizeToContents )
{
- //need to resize box
- QRectF targetRect = QRectF( pos().x(), pos().y(), rect().width(), rect().height() );
- if ( size.height() > targetRect.height() )
- targetRect.setHeight( size.height() );
- if ( size.width() > rect().width() )
- targetRect.setWidth( size.width() );
-
- //set new rect, respecting position mode and data defined size/position
- setSceneRect( evalItemRect( targetRect, true ) );
+ // set a clip region to crop out parts of legend which don't fit
+ QRectF thisPaintRect = QRectF( 0, 0, rect().width(), rect().height() );
+ painter->setClipRect( thisPaintRect );
}
legendRenderer.drawLegend( painter );
@@ -170,6 +192,18 @@ QSizeF QgsComposerLegend::paintAndDetermineSize( QPainter* painter )
void QgsComposerLegend::adjustBoxSize()
{
+ if ( !mSizeToContents )
+ return;
+
+ if ( !mInitialMapScaleCalculated )
+ {
+ // this is messy - but until we have painted the item we have no knowledge of the current DPI
+ // and so cannot correctly calculate the map scale. This results in incorrect size calculations
+ // for marker symbols with size in map units, causing the legends to initially expand to huge
+ // sizes if we attempt to calculate the box size first.
+ return;
+ }
+
QgsLegendRenderer legendRenderer( mLegendModel2, mSettings );
QSizeF size = legendRenderer.minimumSize();
QgsDebugMsg( QString( "width = %1 height = %2" ).arg( size.width() ).arg( size.height() ) );
@@ -181,6 +215,15 @@ void QgsComposerLegend::adjustBoxSize()
}
}
+void QgsComposerLegend::setResizeToContents( bool enabled )
+{
+ mSizeToContents = enabled;
+}
+
+bool QgsComposerLegend::resizeToContents() const
+{
+ return mSizeToContents;
+}
void QgsComposerLegend::setCustomLayerTree( QgsLayerTreeGroup* rootGroup )
{
@@ -299,7 +342,9 @@ void QgsComposerLegend::synchronizeWithModel()
void QgsComposerLegend::updateLegend()
{
// take layer list from map renderer (to have legend order)
+ mLegendModel.blockSignals( true );
mLegendModel.setLayerSet( mComposition ? mComposition->mapSettings().layers() : QStringList() );
+ mLegendModel.blockSignals( false );
adjustBoxSize();
updateItem();
}
@@ -342,6 +387,8 @@ bool QgsComposerLegend::writeXML( QDomElement& elem, QDomDocument & doc ) const
composerLegendElem.setAttribute( "wrapChar", mSettings.wrapChar() );
composerLegendElem.setAttribute( "fontColor", mSettings.fontColor().name() );
+ composerLegendElem.setAttribute( "resizeToContents", mSizeToContents );
+
if ( mComposerMap )
{
composerLegendElem.setAttribute( "map", mComposerMap->id() );
@@ -468,6 +515,8 @@ bool QgsComposerLegend::readXML( const QDomElement& itemElem, const QDomDocument
mSettings.setWrapChar( itemElem.attribute( "wrapChar" ) );
+ mSizeToContents = itemElem.attribute( "resizeToContents", "1" ) != "0";
+
//composer map
mLegendFilterByMap = itemElem.attribute( "legendFilterByMap", "0" ).toInt();
if ( !itemElem.attribute( "map" ).isEmpty() )
@@ -663,6 +712,8 @@ void QgsComposerLegend::doUpdateFilterByMap()
}
else
mLegendModel2->setLegendFilterByMap( nullptr );
+
+ mForceResize = true;
}
void QgsComposerLegend::setLegendFilterOutAtlas( bool doFilter )
diff --git a/src/core/composer/qgscomposerlegend.h b/src/core/composer/qgscomposerlegend.h
index 9a4090c..1f950b3 100644
--- a/src/core/composer/qgscomposerlegend.h
+++ b/src/core/composer/qgscomposerlegend.h
@@ -75,6 +75,20 @@ class CORE_EXPORT QgsComposerLegend : public QgsComposerItem
/** Sets item box to the whole content*/
void adjustBoxSize();
+ /** Sets whether the legend should automatically resize to fit its contents.
+ * @param enabled set to false to disable automatic resizing. The legend frame will not
+ * be expanded to fit legend items, and items may be cropped from display.
+ * @see resizeToContents()
+ * @note added in QGIS 3.0
+ */
+ void setResizeToContents( bool enabled );
+
+ /** Returns whether the legend should automatically resize to fit its contents.
+ * @see setResizeToContents()
+ * @note added in QGIS 3.0
+ */
+ bool resizeToContents() const;
+
/** Returns pointer to the legend model*/
//! @deprecated in 2.6 - use modelV2()
Q_DECL_DEPRECATED QgsLegendModel* model() {return &mLegendModel;}
@@ -293,6 +307,15 @@ class CORE_EXPORT QgsComposerLegend : public QgsComposerItem
void doUpdateFilterByMap();
bool mInAtlas;
+
+ //! Will be false until the associated map scale and DPI have been calculated
+ bool mInitialMapScaleCalculated;
+
+ //! Will be true if the legend size should be totally reset at next paint
+ bool mForceResize;
+
+ //! Will be true if the legend should be resized automatically to fit contents
+ bool mSizeToContents;
};
#endif
diff --git a/src/core/composer/qgscomposermap.cpp b/src/core/composer/qgscomposermap.cpp
index 2eda9e0..58bfbe7 100644
--- a/src/core/composer/qgscomposermap.cpp
+++ b/src/core/composer/qgscomposermap.cpp
@@ -34,6 +34,7 @@
#include "qgspallabeling.h"
#include "qgsexpression.h"
#include "qgsvisibilitypresetcollection.h"
+#include "qgsannotation.h"
#include "qgslabel.h"
#include "qgslabelattributes.h"
@@ -2346,18 +2347,19 @@ void QgsComposerMap::drawCanvasItems( QPainter* painter, const QStyleOptionGraph
for ( int i = itemList.size() - 1; i >= 0; --i )
{
currentItem = itemList.at( i );
- //don't draw mapcanvasmap (has z value -10)
- if ( !currentItem || currentItem->data( 0 ).toString() != "AnnotationItem" )
+
+ const QgsAnnotation* annotation = dynamic_cast< const QgsAnnotation* >( currentItem );
+ if ( !annotation )
{
continue;
}
- drawCanvasItem( currentItem, painter, itemStyle );
+ drawCanvasItem( annotation, painter, itemStyle );
}
}
-void QgsComposerMap::drawCanvasItem( QGraphicsItem* item, QPainter* painter, const QStyleOptionGraphicsItem* itemStyle )
+void QgsComposerMap::drawCanvasItem( const QgsAnnotation* annotation, QPainter* painter, const QStyleOptionGraphicsItem* itemStyle )
{
- if ( !item || !mMapCanvas || !item->isVisible() )
+ if ( !annotation || !annotation->showItem() )
{
return;
}
@@ -2365,56 +2367,52 @@ void QgsComposerMap::drawCanvasItem( QGraphicsItem* item, QPainter* painter, con
painter->save();
painter->setRenderHint( QPainter::Antialiasing );
- //determine scale factor according to graphics view dpi
- double scaleFactor = 1.0 / mMapCanvas->logicalDpiX() * 25.4;
+ double scaleFactor = annotation->scaleFactor();
double itemX, itemY;
- QGraphicsItem* parent = item->parentItem();
- if ( !parent )
+ if ( annotation->mapPositionFixed() )
{
- QPointF mapPos = composerMapPosForItem( item );
+ QPointF mapPos = composerMapPosForItem( annotation );
itemX = mapPos.x();
itemY = mapPos.y();
}
- else //place item relative to the parent item
+ else
{
- QPointF itemScenePos = item->scenePos();
- QPointF parentScenePos = parent->scenePos();
-
- QPointF mapPos = composerMapPosForItem( parent );
-
- itemX = mapPos.x() + ( itemScenePos.x() - parentScenePos.x() ) * scaleFactor;
- itemY = mapPos.y() + ( itemScenePos.y() - parentScenePos.y() ) * scaleFactor;
+ itemX = annotation->relativePosition().x() * rect().width();
+ itemY = annotation->relativePosition().y() * rect().height();
}
- painter->translate( itemX, itemY );
+ painter->translate( itemX, itemY );
painter->scale( scaleFactor, scaleFactor );
//a little trick to let the item know that the paint request comes from the composer
- item->setData( 1, "composer" );
- item->paint( painter, itemStyle, nullptr );
- item->setData( 1, "" );
+ const_cast< QgsAnnotation* >( annotation )->setItemData( 1, "composer" );
+ const_cast< QgsAnnotation* >( annotation )->paint( painter, itemStyle, nullptr );
+ const_cast< QgsAnnotation* >( annotation )->setItemData( 1, "" );
+
painter->restore();
}
-QPointF QgsComposerMap::composerMapPosForItem( const QGraphicsItem* item ) const
+QPointF QgsComposerMap::composerMapPosForItem( const QgsAnnotation* annotation ) const
{
- if ( !item || !mMapCanvas )
- {
+ if ( !annotation )
return QPointF( 0, 0 );
- }
- if ( currentMapExtent()->height() <= 0 || currentMapExtent()->width() <= 0 || mMapCanvas->width() <= 0 || mMapCanvas->height() <= 0 )
+ double mapX = 0.0;
+ double mapY = 0.0;
+
+ mapX = annotation->mapPosition().x();
+ mapY = annotation->mapPosition().y();
+ QgsCoordinateReferenceSystem crs = annotation->mapPositionCrs();
+
+ if ( crs != mComposition->mapSettings().destinationCrs() )
{
- return QPointF( 0, 0 );
+ //need to reproject
+ QgsCoordinateTransform t( crs, mComposition->mapSettings().destinationCrs() );
+ double z = 0.0;
+ t.transformInPlace( mapX, mapY, z );
}
- QRectF graphicsSceneRect = mMapCanvas->sceneRect();
- QPointF itemScenePos = item->scenePos();
- QgsRectangle mapRendererExtent = mComposition->mapSettings().visibleExtent();
-
- double mapX = itemScenePos.x() / graphicsSceneRect.width() * mapRendererExtent.width() + mapRendererExtent.xMinimum();
- double mapY = mapRendererExtent.yMaximum() - itemScenePos.y() / graphicsSceneRect.height() * mapRendererExtent.height();
return mapToItemCoords( QPointF( mapX, mapY ) );
}
diff --git a/src/core/composer/qgscomposermap.h b/src/core/composer/qgscomposermap.h
index 2c2aeda..7eef869 100644
--- a/src/core/composer/qgscomposermap.h
+++ b/src/core/composer/qgscomposermap.h
@@ -37,6 +37,7 @@ class QPainter;
class QgsFillSymbolV2;
class QgsLineSymbolV2;
class QgsVectorLayer;
+class QgsAnnotation;
/** \ingroup MapComposer
* \class QgsComposerMap
@@ -943,8 +944,8 @@ class CORE_EXPORT QgsComposerMap : public QgsComposerItem
void transformShift( double& xShift, double& yShift ) const;
void drawCanvasItems( QPainter* painter, const QStyleOptionGraphicsItem* itemStyle );
- void drawCanvasItem( QGraphicsItem* item, QPainter* painter, const QStyleOptionGraphicsItem* itemStyle );
- QPointF composerMapPosForItem( const QGraphicsItem* item ) const;
+ void drawCanvasItem( const QgsAnnotation* item, QPainter* painter, const QStyleOptionGraphicsItem* itemStyle );
+ QPointF composerMapPosForItem( const QgsAnnotation* item ) const;
enum PartType
{
diff --git a/src/core/composer/qgscomposition.cpp b/src/core/composer/qgscomposition.cpp
index 1f7f83a..b4722ca 100644
--- a/src/core/composer/qgscomposition.cpp
+++ b/src/core/composer/qgscomposition.cpp
@@ -1057,6 +1057,8 @@ bool QgsComposition::readXML( const QDomElement& compositionElem, const QDomDocu
updateBounds();
+ emit variablesChanged();
+
return true;
}
@@ -3287,6 +3289,9 @@ void QgsComposition::setDataDefinedProperty( const QgsComposerObject::DataDefine
void QgsComposition::setCustomProperty( const QString& key, const QVariant& value )
{
mCustomProperties.setValue( key, value );
+
+ if ( key.startsWith( "variable" ) )
+ emit variablesChanged();
}
QVariant QgsComposition::customProperty( const QString& key, const QVariant& defaultValue ) const
diff --git a/src/core/composer/qgscomposition.h b/src/core/composer/qgscomposition.h
index 9f30742..c95e61a 100644
--- a/src/core/composer/qgscomposition.h
+++ b/src/core/composer/qgscomposition.h
@@ -1088,6 +1088,11 @@ class CORE_EXPORT QgsComposition : public QGraphicsScene
/** Is emitted when the composition has an updated status bar message for the composer window*/
void statusMsgChanged( const QString& message );
+ /** Emitted whenever the expression variables stored in the composition have been changed.
+ * @note added in QGIS 3.0
+ */
+ void variablesChanged();
+
friend class QgsComposerObject; //for accessing dataDefinedEvaluate, readDataDefinedPropertyMap and writeDataDefinedPropertyMap
friend class QgsComposerModel; //for accessing updateZValues (should not be public)
};
diff --git a/src/core/geometry/qgsgeometry.cpp b/src/core/geometry/qgsgeometry.cpp
index ae3a176..cb4dcc6 100644
--- a/src/core/geometry/qgsgeometry.cpp
+++ b/src/core/geometry/qgsgeometry.cpp
@@ -1596,7 +1596,7 @@ QgsGeometry *QgsGeometry::unaryUnion( const QList<QgsGeometry*> &geometryList )
QList<QgsGeometry*>::const_iterator it = geometryList.constBegin();
for ( ; it != geometryList.constEnd(); ++it )
{
- if ( *it )
+ if ( *it && !(( *it )->isEmpty() ) )
{
geomV2List.append(( *it )->geometry() );
}
diff --git a/src/core/pal/feature.cpp b/src/core/pal/feature.cpp
index 080a6a6..b2cdcaa 100644
--- a/src/core/pal/feature.cpp
+++ b/src/core/pal/feature.cpp
@@ -879,6 +879,13 @@ LabelPosition* FeaturePart::curvedPlacementAtOffset( PointSet* path_positions, d
delete slp;
return nullptr;
}
+ // Shift the character downwards since the draw position is specified at the baseline
+ // and we're calculating the mean line here
+ double dist = 0.9 * li->label_height / 2;
+ if ( orientation < 0 )
+ dist = -dist;
+ start_x += dist * cos( angle + M_PI_2 );
+ start_y -= dist * sin( angle + M_PI_2 );
double render_angle = angle;
@@ -1042,11 +1049,11 @@ int FeaturePart::createCurvedCandidatesAlongLine( QList< LabelPosition* >& lPos,
double angle_avg = atan2( sin_avg / li->char_num, cos_avg / li->char_num );
// displacement
if (( !reversed && ( flags & FLAG_ABOVE_LINE ) ) || ( reversed && ( flags & FLAG_BELOW_LINE ) ) )
- positions.append( _createCurvedCandidate( slp, angle_avg, mLF->distLabel() ) );
+ positions.append( _createCurvedCandidate( slp, angle_avg, mLF->distLabel() + li->label_height / 2 ) );
if ( flags & FLAG_ON_LINE )
- positions.append( _createCurvedCandidate( slp, angle_avg, -li->label_height / 2 ) );
+ positions.append( _createCurvedCandidate( slp, angle_avg, 0 ) );
if (( !reversed && ( flags & FLAG_BELOW_LINE ) ) || ( reversed && ( flags & FLAG_ABOVE_LINE ) ) )
- positions.append( _createCurvedCandidate( slp, angle_avg, -li->label_height - mLF->distLabel() ) );
+ positions.append( _createCurvedCandidate( slp, angle_avg, -li->label_height / 2 - mLF->distLabel() ) );
// delete original candidate
delete slp;
diff --git a/src/core/pal/layer.cpp b/src/core/pal/layer.cpp
index 4334223..604d2d1 100644
--- a/src/core/pal/layer.cpp
+++ b/src/core/pal/layer.cpp
@@ -362,7 +362,7 @@ void Layer::joinConnectedFeatures()
QLinkedList<FeaturePart*>* parts = mConnectedHashtable.value( labelText );
// go one-by-one part, try to merge
- while ( !parts->isEmpty() )
+ while ( !parts->isEmpty() && parts->count() > 1 )
{
// part we'll be checking against other in this round
FeaturePart* partCheck = parts->takeFirst();
@@ -371,24 +371,29 @@ void Layer::joinConnectedFeatures()
if ( otherPart )
{
// remove partCheck from r-tree
- double bmin[2], bmax[2];
- partCheck->getBoundingBox( bmin, bmax );
- mFeatureIndex->Remove( bmin, bmax, partCheck );
- mFeatureParts.removeOne( partCheck );
+ double checkpartBMin[2], checkpartBMax[2];
+ partCheck->getBoundingBox( checkpartBMin, checkpartBMax );
- mConnectedFeaturesIds.insert( partCheck->featureId(), connectedFeaturesId );
- otherPart->getBoundingBox( bmin, bmax );
+ double otherPartBMin[2], otherPartBMax[2];
+ otherPart->getBoundingBox( otherPartBMin, otherPartBMax );
// merge points from partCheck to p->item
if ( otherPart->mergeWithFeaturePart( partCheck ) )
{
+ // remove the parts we are joining from the index
+ mFeatureIndex->Remove( checkpartBMin, checkpartBMax, partCheck );
+ mFeatureIndex->Remove( otherPartBMin, otherPartBMax, otherPart );
+
+ // reinsert merged line to r-tree (probably not needed)
+ otherPart->getBoundingBox( otherPartBMin, otherPartBMax );
+ mFeatureIndex->Insert( otherPartBMin, otherPartBMax, otherPart );
+
+ mConnectedFeaturesIds.insert( partCheck->featureId(), connectedFeaturesId );
mConnectedFeaturesIds.insert( otherPart->featureId(), connectedFeaturesId );
- // reinsert p->item to r-tree (probably not needed)
- mFeatureIndex->Remove( bmin, bmax, otherPart );
- otherPart->getBoundingBox( bmin, bmax );
- mFeatureIndex->Insert( bmin, bmax, otherPart );
+
+ mFeatureParts.removeOne( partCheck );
+ delete partCheck;
}
- delete partCheck;
}
}
diff --git a/src/core/qgsannotation.h b/src/core/qgsannotation.h
new file mode 100644
index 0000000..5ed3e5d
--- /dev/null
+++ b/src/core/qgsannotation.h
@@ -0,0 +1,90 @@
+/***************************************************************************
+ qgsannotation.h
+ ---------------
+ begin : July 2016
+ copyright : (C) 2016 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 QGSANNOTATION_H
+#define QGSANNOTATION_H
+
+#include "qgspoint.h"
+#include "qgscoordinatereferencesystem.h"
+
+class QPainter;
+class QStyleOptionGraphicsItem;
+
+/** \ingroup core
+ * \class QgsAnnotation
+ * \note added in QGIS 3.0
+ *
+ * \brief An interface for annotation items which are drawn over a map.
+ *
+ * QgsAnnotation is an interface class for map annotation items. These annotations can be
+ * drawn within a map, and have either a fixed map position (retrieved using mapPosition())
+ * or are placed relative to the map's frame (retrieved using relativePosition()).
+ * Annotations with a fixed map position also have a corresponding
+ * QgsCoordinateReferenceSystem, which can be determined by calling mapPositionCrs().
+ */
+
+class CORE_EXPORT QgsAnnotation
+{
+ public:
+
+ //! Returns true if the annotation should be shown.
+ virtual bool showItem() const = 0;
+
+ /** Returns true if the annotation is attached to a fixed map position, or
+ * false if the annotation uses a position relative to the current map
+ * extent.
+ * @see mapPosition()
+ * @see relativePositon()
+ */
+ //TODO QGIS 3 - rename to hasFixedMapPosition()
+ virtual bool mapPositionFixed() const = 0;
+
+ /** Returns the map position of the annotation, if it is attached to a fixed map
+ * position.
+ * @see mapPositionFixed()
+ * @see mapPositionCrs()
+ */
+ virtual QgsPoint mapPosition() const { return QgsPoint(); }
+
+ /** Returns the CRS of the map position, or an invalid CRS if the annotation does
+ * not have a fixed map position.
+ */
+ virtual QgsCoordinateReferenceSystem mapPositionCrs() const { return QgsCoordinateReferenceSystem(); }
+
+ /** Returns the relative position of the annotation, if it is not attached to a fixed map
+ * position. The coordinates in the return point should be between 0 and 1, and represent
+ * the relative percentage for the position compared to the map width and height.
+ * @see mapPositionFixed()
+ */
+ virtual QPointF relativePosition() const { return QPointF(); }
+
+ /** Returns a scaling factor which should be applied to painters before rendering
+ * the item.
+ */
+ virtual double scaleFactor() const = 0;
+
+ //! deprecated - do not use
+ // TODO QGIS 3.0 - remove
+ virtual void setItemData( int role, const QVariant& value ) = 0;
+
+ //! Paint the annotation to a destination painter
+ virtual void paint( QPainter * painter, const QStyleOptionGraphicsItem * option, QWidget * widget = nullptr ) = 0;
+
+
+};
+
+#endif // QGSANNOTATION_H
diff --git a/src/core/qgsapplication.cpp b/src/core/qgsapplication.cpp
index a1ca088..c9f0d45 100644
--- a/src/core/qgsapplication.cpp
+++ b/src/core/qgsapplication.cpp
@@ -1361,3 +1361,8 @@ void QgsApplication::setMaxThreads( int maxThreads )
QgsDebugMsg( QString( "set QThreadPool max thread count to %1" ).arg( QThreadPool::globalInstance()->maxThreadCount() ) );
}
+void QgsApplication::emitSettingsChanged()
+{
+ emit settingsChanged();
+}
+
diff --git a/src/core/qgsapplication.h b/src/core/qgsapplication.h
index 5b1bdf4..2011cc5 100644
--- a/src/core/qgsapplication.h
+++ b/src/core/qgsapplication.h
@@ -363,10 +363,26 @@ class CORE_EXPORT QgsApplication : public QApplication
}
#endif
+ public slots:
+
+ /** Causes the application instance to emit the settingsChanged() signal. This should
+ * be called whenever global, application-wide settings are altered to advise listeners
+ * that they may need to update their state.
+ * @see settingsChanged()
+ * @note added in QGIS 3.0
+ */
+ void emitSettingsChanged();
+
signals:
//! @note not available in python bindings
void preNotify( QObject * receiver, QEvent * event, bool * done );
+ /** Emitted whenever any global, application-wide settings are changed.
+ * @note added in QGIS 3.0
+ * @see emitSettingsChanged()
+ */
+ void settingsChanged();
+
private:
static void copyPath( const QString& src, const QString& dst );
static QObject* ABISYM( mFileOpenEventReceiver );
diff --git a/src/core/qgsexpressioncontext.cpp b/src/core/qgsexpressioncontext.cpp
index 1e1827a..1697911 100644
--- a/src/core/qgsexpressioncontext.cpp
+++ b/src/core/qgsexpressioncontext.cpp
@@ -307,6 +307,19 @@ int QgsExpressionContext::indexOfScope( QgsExpressionContextScope* scope ) const
return mStack.indexOf( scope );
}
+int QgsExpressionContext::indexOfScope( const QString& scopeName ) const
+{
+ int index = 0;
+ Q_FOREACH ( const QgsExpressionContextScope* scope, mStack )
+ {
+ if ( scope->name() == scopeName )
+ return index;
+
+ index++;
+ }
+ return -1;
+}
+
QStringList QgsExpressionContext::variableNames() const
{
QStringList names;
diff --git a/src/core/qgsexpressioncontext.h b/src/core/qgsexpressioncontext.h
index 64b2b9d..6d511e2 100644
--- a/src/core/qgsexpressioncontext.h
+++ b/src/core/qgsexpressioncontext.h
@@ -324,6 +324,13 @@ class CORE_EXPORT QgsExpressionContext
*/
int indexOfScope( QgsExpressionContextScope* scope ) const;
+ /** Returns the index of the first scope with a matching name within the context.
+ * @param scopeName name of scope to find
+ * @returns index of scope, or -1 if scope was not found within the context.
+ * @note added in QGIS 3.0
+ */
+ int indexOfScope( const QString& scopeName ) const;
+
/** Returns a list of variables names set by all scopes in the context.
* @returns list of unique variable names
* @see filteredVariableNames
diff --git a/src/core/qgsmaplayerregistry.cpp b/src/core/qgsmaplayerregistry.cpp
index f13a913..f596668 100644
--- a/src/core/qgsmaplayerregistry.cpp
+++ b/src/core/qgsmaplayerregistry.cpp
@@ -73,12 +73,11 @@ QList<QgsMapLayer *> QgsMapLayerRegistry::addMapLayers(
bool takeOwnership )
{
QList<QgsMapLayer *> myResultList;
- for ( int i = 0; i < theMapLayers.size(); ++i )
+ Q_FOREACH ( QgsMapLayer* myLayer, theMapLayers )
{
- QgsMapLayer * myLayer = theMapLayers.at( i );
if ( !myLayer || !myLayer->isValid() )
{
- QgsDebugMsg( "cannot add invalid layers" );
+ QgsDebugMsg( "Cannot add invalid layers" );
continue;
}
//check the layer is not already registered!
@@ -87,7 +86,10 @@ QList<QgsMapLayer *> QgsMapLayerRegistry::addMapLayers(
mMapLayers[myLayer->id()] = myLayer;
myResultList << mMapLayers[myLayer->id()];
if ( takeOwnership )
- mOwnedLayers << myLayer;
+ {
+ myLayer->setParent( this );
+ }
+ connect( myLayer, SIGNAL( destroyed( QObject* ) ), this, SLOT( onMapLayerDeleted( QObject* ) ) );
emit layerWasAdded( myLayer );
}
}
@@ -151,12 +153,11 @@ void QgsMapLayerRegistry::removeMapLayers( const QList<QgsMapLayer*>& layers )
QString myId( lyr->id() );
emit layerWillBeRemoved( myId );
emit layerWillBeRemoved( lyr );
- if ( mOwnedLayers.contains( lyr ) )
+ mMapLayers.remove( myId );
+ if ( lyr->parent() == this )
{
delete lyr;
- mOwnedLayers.remove( lyr );
}
- mMapLayers.remove( myId );
emit layerRemoved( myId );
}
@@ -185,14 +186,20 @@ void QgsMapLayerRegistry::removeAllMapLayers()
void QgsMapLayerRegistry::reloadAllLayers()
{
- QMap<QString, QgsMapLayer *>::iterator it;
- for ( it = mMapLayers.begin(); it != mMapLayers.end() ; ++it )
+ Q_FOREACH ( QgsMapLayer* layer, mMapLayers )
{
- QgsMapLayer* layer = it.value();
- if ( layer )
- {
- layer->reload();
- }
+ layer->reload();
+ }
+}
+
+void QgsMapLayerRegistry::onMapLayerDeleted( QObject* obj )
+{
+ QString id = mMapLayers.key( static_cast<QgsMapLayer*>( obj ) );
+
+ if ( !id.isNull() )
+ {
+ QgsDebugMsg( QString( "Map layer deleted without unregistering! %1" ).arg( id ) );
+ mMapLayers.remove( id );
}
}
diff --git a/src/core/qgsmaplayerregistry.h b/src/core/qgsmaplayerregistry.h
index f8e3f21..7b16f7f 100644
--- a/src/core/qgsmaplayerregistry.h
+++ b/src/core/qgsmaplayerregistry.h
@@ -283,12 +283,14 @@ class CORE_EXPORT QgsMapLayerRegistry : public QObject
void connectNotify( const char * signal ) override;
#endif
+ private slots:
+ void onMapLayerDeleted( QObject* obj );
+
private:
//! private singleton constructor
QgsMapLayerRegistry( QObject * parent = nullptr );
QMap<QString, QgsMapLayer*> mMapLayers;
- QSet<QgsMapLayer*> mOwnedLayers;
}; // class QgsMapLayerRegistry
#endif //QgsMapLayerRegistry_H
diff --git a/src/core/qgsnetworkaccessmanager.cpp b/src/core/qgsnetworkaccessmanager.cpp
index 2111cde..aa9aba8 100644
--- a/src/core/qgsnetworkaccessmanager.cpp
+++ b/src/core/qgsnetworkaccessmanager.cpp
@@ -372,10 +372,10 @@ void QgsNetworkAccessManager::setupDefaultProxyAndCache()
if ( !newcache )
newcache = new QgsNetworkDiskCache( this );
- QString cacheDirectory = settings.value( "cache/directory", QgsApplication::qgisSettingsDirPath() + "cache" ).toString();
+ QString cacheDirectory = settings.value( "cache/directory" ).toString();
+ if ( cacheDirectory.isEmpty() )
+ cacheDirectory = QgsApplication::qgisSettingsDirPath() + "cache";
qint64 cacheSize = settings.value( "cache/size", 50 * 1024 * 1024 ).toULongLong();
- QgsDebugMsg( QString( "setCacheDirectory: %1" ).arg( cacheDirectory ) );
- QgsDebugMsg( QString( "setMaximumCacheSize: %1" ).arg( cacheSize ) );
newcache->setCacheDirectory( cacheDirectory );
newcache->setMaximumCacheSize( cacheSize );
QgsDebugMsg( QString( "cacheDirectory: %1" ).arg( newcache->cacheDirectory() ) );
diff --git a/src/core/qgsnetworkaccessmanager.h b/src/core/qgsnetworkaccessmanager.h
index e388e4a..b169f19 100644
--- a/src/core/qgsnetworkaccessmanager.h
+++ b/src/core/qgsnetworkaccessmanager.h
@@ -83,7 +83,7 @@ class CORE_EXPORT QgsNetworkAccessManager : public QNetworkAccessManager
void setupDefaultProxyAndCache();
//! return whether the system proxy should be used
- bool useSystemProxy() { return mUseSystemProxy; }
+ bool useSystemProxy() const { return mUseSystemProxy; }
public slots:
/** Send GET request, calls get().
diff --git a/src/core/qgsofflineediting.cpp b/src/core/qgsofflineediting.cpp
index cfb6015..dc21f4c 100644
--- a/src/core/qgsofflineediting.cpp
+++ b/src/core/qgsofflineediting.cpp
@@ -134,15 +134,18 @@ bool QgsOfflineEditing::convertToOfflineProject( const QString& offlineDataPath,
QgsMapLayer* layer = QgsMapLayerRegistry::instance()->mapLayer( layerIds.at( i ) );
QgsVectorLayer* vl = qobject_cast<QgsVectorLayer*>( layer );
- QString origLayerId = vl->id();
- QgsVectorLayer* newLayer = copyVectorLayer( vl, db, dbPath );
-
- if ( newLayer )
+ if ( vl )
{
- layerIdMapping.insert( origLayerId, newLayer );
- // remove remote layer
- QgsMapLayerRegistry::instance()->removeMapLayers(
- QStringList() << origLayerId );
+ QString origLayerId = vl->id();
+ QgsVectorLayer* newLayer = copyVectorLayer( vl, db, dbPath );
+
+ if ( newLayer )
+ {
+ layerIdMapping.insert( origLayerId, newLayer );
+ // remove remote layer
+ QgsMapLayerRegistry::instance()->removeMapLayers(
+ QStringList() << origLayerId );
+ }
}
}
@@ -181,7 +184,7 @@ bool QgsOfflineEditing::convertToOfflineProject( const QString& offlineDataPath,
projectTitle += " (offline)";
QgsProject::instance()->setTitle( projectTitle );
- QgsProject::instance()->writeEntry( PROJECT_ENTRY_SCOPE_OFFLINE, PROJECT_ENTRY_KEY_OFFLINE_DB_PATH, dbPath );
+ QgsProject::instance()->writeEntry( PROJECT_ENTRY_SCOPE_OFFLINE, PROJECT_ENTRY_KEY_OFFLINE_DB_PATH, QgsProject::instance()->writePath( dbPath ) );
return true;
}
@@ -656,11 +659,11 @@ QgsVectorLayer* QgsOfflineEditing::copyVectorLayer( QgsVectorLayer* layer, sqlit
// Check if the online feature has been fetched (WFS download aborted for some reason)
if ( i < offlineFeatureIds.count() )
{
- addFidLookup( db, layerId, offlineFeatureIds.at( i ), remoteFeatureIds.at( remoteCount - ( i + 1 ) ) );
+ addFidLookup( db, layerId, offlineFeatureIds.at( i ), remoteFeatureIds.at( i ) );
}
else
{
- showWarning( QString( "Feature cannot be copied to the offline layer, please check if the online layer '%1' is sill accessible." ).arg( layer->name() ) );
+ showWarning( tr( "Feature cannot be copied to the offline layer, please check if the online layer '%1' is still accessible." ).arg( layer->name() ) );
return nullptr;
}
emit progressUpdated( featureCount++ );
@@ -915,7 +918,8 @@ sqlite3* QgsOfflineEditing::openLoggingDb()
QString dbPath = QgsProject::instance()->readEntry( PROJECT_ENTRY_SCOPE_OFFLINE, PROJECT_ENTRY_KEY_OFFLINE_DB_PATH );
if ( !dbPath.isEmpty() )
{
- int rc = sqlite3_open( dbPath.toUtf8().constData(), &db );
+ QString absoluteDbPath = QgsProject::instance()->readPath( dbPath );
+ int rc = sqlite3_open( absoluteDbPath.toUtf8().constData(), &db );
if ( rc != SQLITE_OK )
{
showWarning( tr( "Could not open the spatialite logging database" ) );
diff --git a/src/core/qgspallabeling.cpp b/src/core/qgspallabeling.cpp
index d88b771..fb7c994 100644
--- a/src/core/qgspallabeling.cpp
+++ b/src/core/qgspallabeling.cpp
@@ -2498,7 +2498,8 @@ void QgsPalLayerSettings::registerFeature( QgsFeature& f, QgsRenderContext &cont
}
GEOSGeometry* geos_geom_clone;
- if ( GEOSGeomTypeId_r( QgsGeometry::getGEOSHandler(), geos_geom ) == GEOS_POLYGON && repeatDistance > 0 && placement == Line )
+ GEOSGeomTypes geomType = ( GEOSGeomTypes ) GEOSGeomTypeId_r( QgsGeometry::getGEOSHandler(), geos_geom );
+ if (( geomType == GEOS_POLYGON || geomType == GEOS_MULTIPOLYGON ) && repeatDistance > 0 && placement == Line )
{
geos_geom_clone = GEOSBoundary_r( QgsGeometry::getGEOSHandler(), geos_geom );
}
@@ -3320,7 +3321,7 @@ void QgsPalLayerSettings::parseTextStyle( QFont& labelFont,
wordspace = wspacing;
}
}
- labelFont.setWordSpacing( sizeToPixel( wordspace, context, fontunits, false, fontSizeMapUnitScale ) );
+ labelFont.setWordSpacing( scaleToPixelContext( wordspace, context, fontunits, false, fontSizeMapUnitScale ) );
// data defined letter spacing?
double letterspace = labelFont.letterSpacing();
@@ -3334,7 +3335,7 @@ void QgsPalLayerSettings::parseTextStyle( QFont& labelFont,
letterspace = lspacing;
}
}
- labelFont.setLetterSpacing( QFont::AbsoluteSpacing, sizeToPixel( letterspace, context, fontunits, false, fontSizeMapUnitScale ) );
+ labelFont.setLetterSpacing( QFont::AbsoluteSpacing, scaleToPixelContext( letterspace, context, fontunits, false, fontSizeMapUnitScale ) );
// data defined font capitalization?
QFont::Capitalization fontcaps = labelFont.capitalization();
diff --git a/src/core/qgsproject.cpp b/src/core/qgsproject.cpp
index d30d4b1..ad8133e 100644
--- a/src/core/qgsproject.cpp
+++ b/src/core/qgsproject.cpp
@@ -417,6 +417,10 @@ void QgsProject::setDirty( bool b )
dirty( b );
}
+void QgsProject::emitVariablesChanged()
+{
+ emit variablesChanged();
+}
void QgsProject::setFileName( QString const &name )
diff --git a/src/core/qgsproject.h b/src/core/qgsproject.h
index 79c0cbd..34cbe08 100644
--- a/src/core/qgsproject.h
+++ b/src/core/qgsproject.h
@@ -391,6 +391,20 @@ class CORE_EXPORT QgsProject : public QObject
void snapSettingsChanged();
+ /** Emitted whenever the expression variables stored in the project have been changed.
+ * @note added in QGIS 3.0
+ */
+ void variablesChanged();
+
+ public slots:
+
+ /** Causes the project to emit the variablesChanged() signal. This should
+ * be called whenever expression variables related to the project are changed.
+ * @see variablesChanged()
+ * @note added in QGIS 3.0
+ */
+ void emitVariablesChanged();
+
private:
QgsProject(); // private 'cause it's a singleton
diff --git a/src/gui/editorwidgets/qgsrelationreferencewidget.cpp b/src/gui/editorwidgets/qgsrelationreferencewidget.cpp
index b3fc72c..93b99aa 100644
--- a/src/gui/editorwidgets/qgsrelationreferencewidget.cpp
+++ b/src/gui/editorwidgets/qgsrelationreferencewidget.cpp
@@ -707,7 +707,7 @@ void QgsRelationReferenceWidget::featureIdentified( const QgsFeature& feature )
}
else
{
- mComboBox->setCurrentIndex( mComboBox->findData( feature.attribute( mReferencedFieldIdx ), QgsAttributeTableModel::FeatureIdRole ) );
+ mComboBox->setCurrentIndex( mComboBox->findData( feature.id(), QgsAttributeTableModel::FeatureIdRole ) );
mFeature = feature;
}
diff --git a/src/gui/qgsannotationitem.cpp b/src/gui/qgsannotationitem.cpp
index d2c9462..e63ea74 100644
--- a/src/gui/qgsannotationitem.cpp
+++ b/src/gui/qgsannotationitem.cpp
@@ -54,7 +54,19 @@ void QgsAnnotationItem::setMapPosition( const QgsPoint& pos )
{
mMapPosition = pos;
setPos( toCanvasCoordinates( mMapPosition ) );
- mMapPositionCrs = mMapCanvas->mapSettings().destinationCrs();
+ setMapPositionCrs( mMapCanvas->mapSettings().destinationCrs() );
+}
+
+QPointF QgsAnnotationItem::relativePosition() const
+{
+ double x = pos().x() / mMapCanvas->width();
+ double y = pos().y() / mMapCanvas->height();
+ return QPointF( x, y );
+}
+
+double QgsAnnotationItem::scaleFactor() const
+{
+ return 1.0 / mMapCanvas->logicalDpiX() * 25.4;
}
void QgsAnnotationItem::setMapPositionCrs( const QgsCoordinateReferenceSystem& crs )
@@ -185,7 +197,7 @@ void QgsAnnotationItem::updateBalloon()
mBalloonSegmentPoint2 = pointOnLineWithDistance( mBalloonSegmentPoint1, minEdge.p2(), 10 );
}
-void QgsAnnotationItem::drawFrame( QPainter* p )
+void QgsAnnotationItem::drawFrame( QPainter* p ) const
{
QPen framePen( mFrameColor );
framePen.setWidthF( mFrameBorderWidth );
@@ -219,7 +231,7 @@ void QgsAnnotationItem::setFrameSize( QSizeF size )
updateBalloon();
}
-void QgsAnnotationItem::drawMarkerSymbol( QPainter* p )
+void QgsAnnotationItem::drawMarkerSymbol( QPainter* p ) const
{
if ( !p )
{
@@ -240,7 +252,7 @@ void QgsAnnotationItem::drawMarkerSymbol( QPainter* p )
}
}
-void QgsAnnotationItem::drawSelectionBoxes( QPainter* p )
+void QgsAnnotationItem::drawSelectionBoxes( QPainter* p ) const
{
if ( !p )
{
@@ -262,7 +274,7 @@ void QgsAnnotationItem::drawSelectionBoxes( QPainter* p )
p->drawRect( QRectF( mBoundingRect.left(), mBoundingRect.bottom() - handlerSize, handlerSize, handlerSize ) );
}
-QLineF QgsAnnotationItem::segment( int index )
+QLineF QgsAnnotationItem::segment( int index ) const
{
switch ( index )
{
@@ -407,12 +419,12 @@ void QgsAnnotationItem::_writeXML( QDomDocument& doc, QDomElement& itemElem ) co
mMapPositionCrs.writeXML( annotationElem, doc );
annotationElem.setAttribute( "offsetX", qgsDoubleToString( mOffsetFromReferencePoint.x() ) );
annotationElem.setAttribute( "offsetY", qgsDoubleToString( mOffsetFromReferencePoint.y() ) );
- annotationElem.setAttribute( "frameWidth", QString::number( mFrameSize.width() ) );
- annotationElem.setAttribute( "frameHeight", QString::number( mFrameSize.height() ) );
+ annotationElem.setAttribute( "frameWidth", qgsDoubleToString( mFrameSize.width() ) );
+ annotationElem.setAttribute( "frameHeight", qgsDoubleToString( mFrameSize.height() ) );
QPointF canvasPos = pos();
annotationElem.setAttribute( "canvasPosX", qgsDoubleToString( canvasPos.x() ) );
annotationElem.setAttribute( "canvasPosY", qgsDoubleToString( canvasPos.y() ) );
- annotationElem.setAttribute( "frameBorderWidth", QString::number( mFrameBorderWidth ) );
+ annotationElem.setAttribute( "frameBorderWidth", qgsDoubleToString( mFrameBorderWidth ) );
annotationElem.setAttribute( "frameColor", mFrameColor.name() );
annotationElem.setAttribute( "frameColorAlpha", mFrameColor.alpha() );
annotationElem.setAttribute( "frameBackgroundColor", mFrameBackgroundColor.name() );
@@ -444,8 +456,12 @@ void QgsAnnotationItem::_readXML( const QDomDocument& doc, const QDomElement& an
mapPos.setX( annotationElem.attribute( "mapPosX", "0" ).toDouble() );
mapPos.setY( annotationElem.attribute( "mapPosY", "0" ).toDouble() );
mMapPosition = mapPos;
+
if ( !mMapPositionCrs.readXML( annotationElem ) )
+ {
mMapPositionCrs = mMapCanvas->mapSettings().destinationCrs();
+ }
+
mFrameBorderWidth = annotationElem.attribute( "frameBorderWidth", "0.5" ).toDouble();
mFrameColor.setNamedColor( annotationElem.attribute( "frameColor", "#000000" ) );
mFrameColor.setAlpha( annotationElem.attribute( "frameColorAlpha", "255" ).toInt() );
@@ -473,3 +489,19 @@ void QgsAnnotationItem::_readXML( const QDomDocument& doc, const QDomElement& an
updateBoundingRect();
updateBalloon();
}
+
+void QgsAnnotationItem::setItemData( int role, const QVariant& value )
+{
+ setData( role, value );
+}
+
+void QgsAnnotationItem::paint( QPainter* painter, const QStyleOptionGraphicsItem*, QWidget* )
+{
+ // maintain API compatibility, if annotation item subclasses only implement the paint( QPainter* ) override
+ paint( painter );
+}
+
+void QgsAnnotationItem::paint( QPainter* painter )
+{
+ Q_UNUSED( painter );
+}
diff --git a/src/gui/qgsannotationitem.h b/src/gui/qgsannotationitem.h
index 4b187c7..e77eab8 100644
--- a/src/gui/qgsannotationitem.h
+++ b/src/gui/qgsannotationitem.h
@@ -20,6 +20,7 @@
#include "qgsmapcanvasitem.h"
#include "qgscoordinatereferencesystem.h"
+#include "qgsannotation.h"
class QDomDocument;
class QDomElement;
@@ -29,7 +30,8 @@ class QgsMarkerSymbolV2;
/** An annotation item can be either placed either on screen corrdinates or on map coordinates.
It may reference a feature and displays that associatiation with a balloon like appearance*/
-class GUI_EXPORT QgsAnnotationItem: public QgsMapCanvasItem
+
+class GUI_EXPORT QgsAnnotationItem: public QgsMapCanvasItem, public QgsAnnotation
{
public:
enum MouseMoveAction
@@ -64,16 +66,22 @@ class GUI_EXPORT QgsAnnotationItem: public QgsMapCanvasItem
//setters and getters
void setMapPositionFixed( bool fixed );
- bool mapPositionFixed() const { return mMapPositionFixed; }
+ bool mapPositionFixed() const override { return mMapPositionFixed; }
virtual void setMapPosition( const QgsPoint& pos );
- QgsPoint mapPosition() const { return mMapPosition; }
+ QgsPoint mapPosition() const override { return mMapPosition; }
+
+ virtual QPointF relativePosition() const override;
+
+ virtual double scaleFactor() const override;
+
+ virtual bool showItem() const override { return isVisible(); }
/** Sets the CRS of the map position.
@param crs the CRS to set */
virtual void setMapPositionCrs( const QgsCoordinateReferenceSystem& crs );
/** Returns the CRS of the map position.*/
- QgsCoordinateReferenceSystem mapPositionCrs() const { return mMapPositionCrs; }
+ QgsCoordinateReferenceSystem mapPositionCrs() const override { return mMapPositionCrs; }
void setFrameSize( QSizeF size );
QSizeF frameSize() const { return mFrameSize; }
@@ -100,6 +108,12 @@ class GUI_EXPORT QgsAnnotationItem: public QgsMapCanvasItem
void _writeXML( QDomDocument& doc, QDomElement& itemElem ) const;
void _readXML( const QDomDocument& doc, const QDomElement& annotationElem );
+ virtual void setItemData( int role, const QVariant& value ) override;
+
+ virtual void paint( QPainter * painter, const QStyleOptionGraphicsItem * option, QWidget * widget = nullptr ) override;
+
+ void paint( QPainter* painter ) override;
+
protected:
/** True: the item stays at the same map position, False: the item stays on same screen position*/
bool mMapPositionFixed;
@@ -135,13 +149,19 @@ class GUI_EXPORT QgsAnnotationItem: public QgsMapCanvasItem
/** Check where to attach the balloon connection between frame and map point*/
void updateBalloon();
- void drawFrame( QPainter* p );
- void drawMarkerSymbol( QPainter* p );
- void drawSelectionBoxes( QPainter* p );
+ //! Draws the annotation frame to a destination painter
+ void drawFrame( QPainter* p ) const;
+
+ //! Draws the map position marker symbol to a destination painter
+ void drawMarkerSymbol( QPainter* p ) const;
+
+ //! Draws selection handles around the item
+ void drawSelectionBoxes( QPainter* p ) const;
+
/** Returns frame width in painter units*/
//double scaledFrameWidth( QPainter* p) const;
/** Gets the frame line (0 is the top line, 1 right, 2 bottom, 3 left)*/
- QLineF segment( int index );
+ QLineF segment( int index ) const;
/** Returns a point on the line from startPoint to directionPoint that is a certain distance away from the starting point*/
QPointF pointOnLineWithDistance( QPointF startPoint, QPointF directionPoint, double distance ) const;
/** Returns the symbol size scaled in (mapcanvas) pixels. Used for the counding rect calculation*/
diff --git a/src/gui/qgscredentialdialog.cpp b/src/gui/qgscredentialdialog.cpp
index 9394ec5..7887aca 100644
--- a/src/gui/qgscredentialdialog.cpp
+++ b/src/gui/qgscredentialdialog.cpp
@@ -43,6 +43,7 @@ QgsCredentialDialog::QgsCredentialDialog( QWidget *parent, const Qt::WindowFlags
Qt::BlockingQueuedConnection );
mOkButton = buttonBox->button( QDialogButtonBox::Ok );
leMasterPass->setPlaceholderText( tr( "Required" ) );
+ leUsername->setFocus();
}
QgsCredentialDialog::~QgsCredentialDialog()
@@ -119,6 +120,7 @@ void QgsCredentialDialog::requestCredentialsMasterPassword( QString * password,
{
QgsDebugMsg( "Entering." );
stackedWidget->setCurrentIndex( 1 );
+ leMasterPass->setFocus();
QString titletxt( stored ? tr( "Enter CURRENT master authentication password" ) : tr( "Set NEW master authentication password" ) );
lblPasswordTitle->setText( titletxt );
diff --git a/src/gui/qgsvariableeditorwidget.h b/src/gui/qgsvariableeditorwidget.h
index f28a511..85b1141 100644
--- a/src/gui/qgsvariableeditorwidget.h
+++ b/src/gui/qgsvariableeditorwidget.h
@@ -65,12 +65,6 @@ class GUI_EXPORT QgsVariableEditorWidget : public QWidget
*/
QgsExpressionContext* context() const { return mContext.data(); }
- /** Reloads all scopes from the editor's current context. This method should be called
- * after adding or removing scopes from the attached context.
- * @see context()
- */
- void reloadContext();
-
/** Sets the editable scope for the widget. Only variables from the editable scope can
* be modified by users.
* @param scopeIndex index of current editable scope. Set to -1 to disable
@@ -107,6 +101,14 @@ class GUI_EXPORT QgsVariableEditorWidget : public QWidget
*/
QgsStringMap variablesInActiveScope() const;
+ public slots:
+
+ /** Reloads all scopes from the editor's current context. This method should be called
+ * after adding or removing scopes from the attached context.
+ * @see context()
+ */
+ void reloadContext();
+
signals:
/** Emitted when the user has modified a scope using the widget.
diff --git a/src/plugins/heatmap/heatmap.cpp b/src/plugins/heatmap/heatmap.cpp
index d753adb..95138ec 100644
--- a/src/plugins/heatmap/heatmap.cpp
+++ b/src/plugins/heatmap/heatmap.cpp
@@ -170,7 +170,10 @@ void Heatmap::run()
// Write the empty raster
for ( int i = 0; i < rows ; i++ )
{
- poBand->RasterIO( GF_Write, 0, i, columns, 1, line, columns, 1, GDT_Float32, 0, 0 );
+ if ( poBand->RasterIO( GF_Write, 0, i, columns, 1, line, columns, 1, GDT_Float32, 0, 0 ) != CE_None )
+ {
+ QgsDebugMsg( "Raster IO Error" );
+ }
}
CPLFree( line );
@@ -299,8 +302,11 @@ void Heatmap::run()
// get the data
float *dataBuffer = ( float * ) CPLMalloc( sizeof( float ) * blockSize * blockSize );
- poBand->RasterIO( GF_Read, xPosition, yPosition, blockSize, blockSize,
- dataBuffer, blockSize, blockSize, GDT_Float32, 0, 0 );
+ if ( poBand->RasterIO( GF_Read, xPosition, yPosition, blockSize, blockSize,
+ dataBuffer, blockSize, blockSize, GDT_Float32, 0, 0 ) != CE_None )
+ {
+ QgsDebugMsg( "Raster IO Error" );
+ }
for ( int xp = 0; xp <= myBuffer; xp++ )
{
@@ -341,8 +347,11 @@ void Heatmap::run()
}
}
}
- poBand->RasterIO( GF_Write, xPosition, yPosition, blockSize, blockSize,
- dataBuffer, blockSize, blockSize, GDT_Float32, 0, 0 );
+ if ( poBand->RasterIO( GF_Write, xPosition, yPosition, blockSize, blockSize,
+ dataBuffer, blockSize, blockSize, GDT_Float32, 0, 0 ) != CE_None )
+ {
+ QgsDebugMsg( "Raster IO Error" );
+ }
CPLFree( dataBuffer );
}
}
diff --git a/src/providers/oracle/ocispatial/qsql_ocispatial.cpp b/src/providers/oracle/ocispatial/qsql_ocispatial.cpp
index 7059aca..c20ad29 100644
--- a/src/providers/oracle/ocispatial/qsql_ocispatial.cpp
+++ b/src/providers/oracle/ocispatial/qsql_ocispatial.cpp
@@ -658,6 +658,7 @@ int QOCISpatialResultPrivate::bindValue( OCIStmt *sql, OCIBind **hbnd, OCIError
ba.data(),
ba.capacity(),
SQLT_STR, indPtr, tmpSize, 0, 0, 0, OCI_DEFAULT );
+ tmpStorage.append( ba );
}
else
{
@@ -669,7 +670,6 @@ int QOCISpatialResultPrivate::bindValue( OCIStmt *sql, OCIBind **hbnd, OCIError
}
if ( r == OCI_SUCCESS )
setCharset( *hbnd, OCI_HTYPE_BIND );
- tmpStorage.append( ba );
break;
} // default case
} // switch
diff --git a/src/providers/oracle/qgsoracleconn.cpp b/src/providers/oracle/qgsoracleconn.cpp
index 2fa7d40..536efae 100644
--- a/src/providers/oracle/qgsoracleconn.cpp
+++ b/src/providers/oracle/qgsoracleconn.cpp
@@ -31,7 +31,7 @@ QMap<QString, QgsOracleConn *> QgsOracleConn::sConnections;
int QgsOracleConn::snConnections = 0;
const int QgsOracleConn::sGeomTypeSelectLimit = 100;
-QgsOracleConn *QgsOracleConn::connectDb( QgsDataSourceURI uri )
+QgsOracleConn *QgsOracleConn::connectDb( const QgsDataSourceURI& uri )
{
QString conninfo = uri.connectionInfo();
diff --git a/src/providers/oracle/qgsoracleconn.h b/src/providers/oracle/qgsoracleconn.h
index 45d3a9a..b0dd1a4 100644
--- a/src/providers/oracle/qgsoracleconn.h
+++ b/src/providers/oracle/qgsoracleconn.h
@@ -45,6 +45,10 @@ struct QgsOracleLayerProperty
QStringList pkCols;
QString sql;
+ QgsOracleLayerProperty()
+ : isView( false )
+ {}
+
int size() const { Q_ASSERT( types.size() == srids.size() ); return types.size(); }
bool operator==( const QgsOracleLayerProperty& other )
@@ -107,7 +111,7 @@ class QgsOracleConn : public QObject
{
Q_OBJECT
public:
- static QgsOracleConn *connectDb( QgsDataSourceURI uri );
+ static QgsOracleConn *connectDb( const QgsDataSourceURI &uri );
void disconnect();
/** Double quote a Oracle identifier for placement in a SQL string.
diff --git a/src/providers/oracle/qgsoracleconnpool.h b/src/providers/oracle/qgsoracleconnpool.h
index 2c5004d..108a52b 100644
--- a/src/providers/oracle/qgsoracleconnpool.h
+++ b/src/providers/oracle/qgsoracleconnpool.h
@@ -25,7 +25,7 @@ inline QString qgsConnectionPool_ConnectionToName( QgsOracleConn* c )
return c->connInfo();
}
-inline void qgsConnectionPool_ConnectionCreate( QgsDataSourceURI uri, QgsOracleConn*& c )
+inline void qgsConnectionPool_ConnectionCreate( const QgsDataSourceURI& uri, QgsOracleConn*& c )
{
c = QgsOracleConn::connectDb( uri );
}
diff --git a/src/providers/oracle/qgsoracleprovider.cpp b/src/providers/oracle/qgsoracleprovider.cpp
index 61e8b5e..3d03f3d 100644
--- a/src/providers/oracle/qgsoracleprovider.cpp
+++ b/src/providers/oracle/qgsoracleprovider.cpp
@@ -1772,7 +1772,6 @@ void QgsOracleProvider::appendGeomParam( const QgsGeometry *geom, QSqlQuery &qry
g.eleminfo.clear();
g.ordinates.clear();
- QString expr;
int iOrdinate = 1;
QGis::WkbType type = ( QGis::WkbType ) * ptr.iPtr++;
int dim = 2;
@@ -1781,6 +1780,8 @@ void QgsOracleProvider::appendGeomParam( const QgsGeometry *geom, QSqlQuery &qry
{
case QGis::WKBPoint25D:
dim = 3;
+ FALLTHROUGH;
+
case QGis::WKBPoint:
g.srid = mSrid;
g.gtype = SDO_GTYPE( dim, gtPoint );
@@ -1792,6 +1793,8 @@ void QgsOracleProvider::appendGeomParam( const QgsGeometry *geom, QSqlQuery &qry
case QGis::WKBLineString25D:
case QGis::WKBMultiLineString25D:
dim = 3;
+ FALLTHROUGH;
+
case QGis::WKBLineString:
case QGis::WKBMultiLineString:
{
@@ -1827,6 +1830,8 @@ void QgsOracleProvider::appendGeomParam( const QgsGeometry *geom, QSqlQuery &qry
case QGis::WKBPolygon25D:
case QGis::WKBMultiPolygon25D:
dim = 3;
+ FALLTHROUGH;
+
case QGis::WKBPolygon:
case QGis::WKBMultiPolygon:
{
@@ -1866,6 +1871,8 @@ void QgsOracleProvider::appendGeomParam( const QgsGeometry *geom, QSqlQuery &qry
case QGis::WKBMultiPoint25D:
dim = 3;
+ FALLTHROUGH;
+
case QGis::WKBMultiPoint:
{
g.gtype = SDO_GTYPE( dim, gtMultiPoint );
@@ -2734,7 +2741,7 @@ QgsVectorLayerImport::ImportError QgsOracleProvider::createEmptyLayer(
QgsDebugMsg( QString( "layer %1 created" ).arg( ownerTableName ) );
- // use the provider to edit the table
+ // use the provider to edit the table1
dsUri.setDataSource( ownerName, tableName, geometryColumn, QString(), primaryKey );
QgsOracleProvider *provider = new QgsOracleProvider( dsUri.uri() );
if ( !provider->isValid() )
@@ -2749,8 +2756,7 @@ QgsVectorLayerImport::ImportError QgsOracleProvider::createEmptyLayer(
QgsDebugMsg( "layer loaded" );
// add fields to the layer
- if ( oldToNewAttrIdxMap )
- oldToNewAttrIdxMap->clear();
+ oldToNewAttrIdxMap->clear();
if ( fields.size() > 0 )
{
diff --git a/src/providers/oracle/qgsoracleprovider.h b/src/providers/oracle/qgsoracleprovider.h
index 89015c0..840ed93 100644
--- a/src/providers/oracle/qgsoracleprovider.h
+++ b/src/providers/oracle/qgsoracleprovider.h
@@ -400,7 +400,6 @@ class QgsOracleProvider : public QgsVectorDataProvider
QgsFeatureId mFidCounter; //! next feature id if map is used
QgsOracleConn *mConnection;
- bool mHasSpatial; //! Oracle Spatial is installed
bool mHasSpatialIndex; //! Geometry column is indexed
QString mSpatialIndexName; //! name of spatial index of geometry column
diff --git a/src/providers/wcs/qgswcsprovider.cpp b/src/providers/wcs/qgswcsprovider.cpp
index cf73afc..9c78f81 100644
--- a/src/providers/wcs/qgswcsprovider.cpp
+++ b/src/providers/wcs/qgswcsprovider.cpp
@@ -587,7 +587,10 @@ void QgsWcsProvider::readBlock( int bandNo, QgsRectangle const & viewExtent, in
QgsDebugMsg( QString( "Couldn't allocate memory of %1 bytes" ).arg( size ) );
return;
}
- GDALRasterIO( gdalBand, GF_Read, 0, 0, width, height, tmpData, width, height, ( GDALDataType ) mGdalDataType.at( bandNo - 1 ), 0, 0 );
+ if ( GDALRasterIO( gdalBand, GF_Read, 0, 0, width, height, tmpData, width, height, ( GDALDataType ) mGdalDataType.at( bandNo - 1 ), 0, 0 ) != CE_None )
+ {
+ QgsDebugMsg( "Raster IO Error" );
+ }
for ( int i = 0; i < pixelHeight; i++ )
{
for ( int j = 0; j < pixelWidth; j++ )
@@ -601,13 +604,22 @@ void QgsWcsProvider::readBlock( int bandNo, QgsRectangle const & viewExtent, in
}
else if ( width == pixelWidth && height == pixelHeight )
{
- GDALRasterIO( gdalBand, GF_Read, 0, 0, pixelWidth, pixelHeight, block, pixelWidth, pixelHeight, ( GDALDataType ) mGdalDataType.at( bandNo - 1 ), 0, 0 );
- QgsDebugMsg( tr( "Block read OK" ) );
+ if ( GDALRasterIO( gdalBand, GF_Read, 0, 0, pixelWidth, pixelHeight, block, pixelWidth, pixelHeight, ( GDALDataType ) mGdalDataType.at( bandNo - 1 ), 0, 0 ) != CE_None )
+ {
+ QgsDebugMsg( "Raster IO Error" );
+ }
+ else
+ {
+ QgsDebugMsg( "Block read OK" );
+ }
}
else
{
// This should not happen, but it is better to give distorted result + warning
- GDALRasterIO( gdalBand, GF_Read, 0, 0, width, height, block, pixelWidth, pixelHeight, ( GDALDataType ) mGdalDataType.at( bandNo - 1 ), 0, 0 );
+ if ( GDALRasterIO( gdalBand, GF_Read, 0, 0, width, height, block, pixelWidth, pixelHeight, ( GDALDataType ) mGdalDataType.at( bandNo - 1 ), 0, 0 ) != CE_None )
+ {
+ QgsDebugMsg( "Raster IO Error" );
+ }
QgsMessageLog::logMessage( tr( "Received coverage has wrong size %1 x %2 (expected %3 x %4)" ).arg( width ).arg( height ).arg( pixelWidth ).arg( pixelHeight ), tr( "WCS" ) );
}
}
diff --git a/src/providers/wms/CMakeLists.txt b/src/providers/wms/CMakeLists.txt
index 310d566..bc19de4 100644
--- a/src/providers/wms/CMakeLists.txt
+++ b/src/providers/wms/CMakeLists.txt
@@ -41,6 +41,7 @@ INCLUDE_DIRECTORIES(SYSTEM
${QCA_INCLUDE_DIR}
)
+ADD_LIBRARY(wmsprovider_a STATIC ${WMS_SRCS} ${WMS_MOC_SRCS})
ADD_LIBRARY(wmsprovider MODULE ${WMS_SRCS} ${WMS_MOC_SRCS})
TARGET_LINK_LIBRARIES(wmsprovider
@@ -50,6 +51,11 @@ TARGET_LINK_LIBRARIES(wmsprovider
${GDAL_LIBRARY} # for OGR_G_CreateGeometryFromJson()
)
+TARGET_LINK_LIBRARIES(wmsprovider_a
+ qgis_core
+ ${QT_QTSCRIPT_LIBRARY}
+)
+
INSTALL (TARGETS wmsprovider
RUNTIME DESTINATION ${QGIS_PLUGIN_DIR}
LIBRARY DESTINATION ${QGIS_PLUGIN_DIR})
diff --git a/src/providers/wms/qgswmscapabilities.cpp b/src/providers/wms/qgswmscapabilities.cpp
index edc33fb..5ce19bf 100644
--- a/src/providers/wms/qgswmscapabilities.cpp
+++ b/src/providers/wms/qgswmscapabilities.cpp
@@ -905,6 +905,17 @@ void QgsWmsCapabilities::parseLayer( QDomElement const & e, QgsWmsLayerProperty&
parseStyle( e1, styleProperty );
+ for ( int i = 0; i < layerProperty.style.size(); ++i )
+ {
+ if ( layerProperty.style.at( i ).name == styleProperty.name )
+ {
+ // override inherited parent's style if it has the same name
+ // according to the WMS spec, it should not happen, but Mapserver
+ // does it all the time.
+ layerProperty.style.remove( i );
+ break;
+ }
+ }
layerProperty.style.push_back( styleProperty );
}
else if ( tagName == "MinScaleDenominator" )
diff --git a/src/providers/wms/qgswmsprovider.cpp b/src/providers/wms/qgswmsprovider.cpp
index a39354c..d6d22bf 100644
--- a/src/providers/wms/qgswmsprovider.cpp
+++ b/src/providers/wms/qgswmsprovider.cpp
@@ -215,6 +215,36 @@ QString QgsWmsProvider::getTileUrl() const
}
}
+static bool isValidLegend( const QgsWmsLegendUrlProperty &l )
+{
+ return l.format.startsWith( "image/" );
+}
+
+/**
+ * Picks a usable legend URL for a given style.
+ */
+static QString pickLegend( const QgsWmsStyleProperty &s )
+{
+ QString url;
+ for ( int k = 0; k < s.legendUrl.size() && url.isEmpty(); k++ )
+ {
+ const QgsWmsLegendUrlProperty &l = s.legendUrl[k];
+ if ( isValidLegend( l ) )
+ {
+ url = l.onlineResource.xlinkHref;
+ }
+ }
+ return url;
+}
+
+static const QgsWmsStyleProperty *searchStyle( const QVector<QgsWmsStyleProperty>& styles, const QString& name )
+{
+ Q_FOREACH ( const QgsWmsStyleProperty &s, styles )
+ if ( s.name == name )
+ return &s;
+ return nullptr;
+}
+
QString QgsWmsProvider::getLegendGraphicUrl() const
{
QString url;
@@ -223,25 +253,31 @@ QString QgsWmsProvider::getLegendGraphicUrl() const
{
const QgsWmsLayerProperty &l = mCaps.mLayersSupported[i];
- if ( l.name != mSettings.mActiveSubLayers[0] )
- continue;
-
- for ( int j = 0; j < l.style.size() && url.isEmpty(); j++ )
+ if ( l.name == mSettings.mActiveSubLayers[0] )
{
- const QgsWmsStyleProperty &s = l.style[j];
-
- if ( s.name != mSettings.mActiveSubStyles[0] )
- continue;
-
- for ( int k = 0; k < s.legendUrl.size() && url.isEmpty(); k++ )
+ if ( !mSettings.mActiveSubStyles[0].isEmpty() && mSettings.mActiveSubStyles[0] != "default" )
{
- const QgsWmsLegendUrlProperty &l = s.legendUrl[k];
-
- if ( l.format != mSettings.mImageMimeType )
- continue;
-
- url = l.onlineResource.xlinkHref;
+ const QgsWmsStyleProperty *s = searchStyle( l.style, mSettings.mActiveSubStyles[0] );
+ if ( s )
+ url = pickLegend( *s );
}
+ else
+ {
+ // QGIS wants the default style, but GetCapabilities doesn't give us a
+ // way to know what is the default style. So we look for the onlineResource
+ // only if there is a single style available or if there is a style called "default".
+ if ( l.style.size() == 1 )
+ {
+ url = pickLegend( l.style[0] );
+ }
+ else
+ {
+ const QgsWmsStyleProperty *s = searchStyle( l.style, "default" );
+ if ( s )
+ url = pickLegend( *s );
+ }
+ }
+ break;
}
}
diff --git a/src/python/qgspythonutilsimpl.cpp b/src/python/qgspythonutilsimpl.cpp
index 5ac0352..126ce33 100644
--- a/src/python/qgspythonutilsimpl.cpp
+++ b/src/python/qgspythonutilsimpl.cpp
@@ -202,13 +202,6 @@ bool QgsPythonUtilsImpl::checkQgisUser()
return true;
}
-void QgsPythonUtilsImpl::doUserImports()
-{
-
- QString startuppath = homePythonPath() + " + \"/startup.py\"";
- runString( "if os.path.exists(" + startuppath + "): from startup import *\n" );
-}
-
void QgsPythonUtilsImpl::initPython( QgisInterface* interface )
{
init();
@@ -224,7 +217,6 @@ void QgsPythonUtilsImpl::initPython( QgisInterface* interface )
exitPython();
return;
}
- doUserImports();
finish();
}
@@ -250,7 +242,6 @@ void QgsPythonUtilsImpl::initServerPython( QgsServerInterface* interface )
// This is the other main difference with initInterface() for desktop plugins
runString( "qgis.utils.initServerInterface(" + QString::number(( unsigned long ) interface ) + ')' );
- doUserImports();
finish();
}
diff --git a/src/python/qgspythonutilsimpl.h b/src/python/qgspythonutilsimpl.h
index f4a8e57..348689f 100644
--- a/src/python/qgspythonutilsimpl.h
+++ b/src/python/qgspythonutilsimpl.h
@@ -126,9 +126,6 @@ class QgsPythonUtilsImpl : public QgsPythonUtils
//@return true if qgis.user could be imported
bool checkQgisUser();
- //! import user defined Python code
- void doUserImports();
-
//! cleanup Python context
void finish();
diff --git a/src/server/qgsowsserver.h b/src/server/qgsowsserver.h
index 4c05f07..48a13e0 100644
--- a/src/server/qgsowsserver.h
+++ b/src/server/qgsowsserver.h
@@ -21,6 +21,8 @@
#include "qgsrequesthandler.h"
#ifdef HAVE_SERVER_PYTHON_PLUGINS
#include "qgsaccesscontrol.h"
+#else
+class QgsMapLayer;
#endif
#include <QHash>
diff --git a/src/server/qgswmsprojectparser.cpp b/src/server/qgswmsprojectparser.cpp
index 056064a..ea47596 100644
--- a/src/server/qgswmsprojectparser.cpp
+++ b/src/server/qgswmsprojectparser.cpp
@@ -1146,6 +1146,11 @@ void QgsWMSProjectParser::addLayers( QDomDocument &doc,
}
}
+ if ( !ltGroup )
+ {
+ QgsDebugMsg( QString( "Skipping group %1, it could not be found" ).arg( name ) );
+ continue;
+ }
QString shortName = ltGroup->customProperty( "wmsShortName" ).toString();
QString title = ltGroup->customProperty( "wmsTitle" ).toString();
diff --git a/src/ui/composer/qgscomposerlegendwidgetbase.ui b/src/ui/composer/qgscomposerlegendwidgetbase.ui
index 5c8b6f9..bbf2f2c 100644
--- a/src/ui/composer/qgscomposerlegendwidgetbase.ui
+++ b/src/ui/composer/qgscomposerlegendwidgetbase.ui
@@ -130,7 +130,7 @@
<item row="1" column="0">
<widget class="QLabel" name="label_15">
<property name="text">
- <string>Title alignment:</string>
+ <string>Title alignment</string>
</property>
</widget>
</item>
@@ -153,6 +153,13 @@
</item>
</widget>
</item>
+ <item row="4" column="0" colspan="2">
+ <widget class="QCheckBox" name="mCheckboxResizeContents">
+ <property name="text">
+ <string>Resize to fit contents</string>
+ </property>
+ </widget>
+ </item>
</layout>
</widget>
</item>
@@ -1029,6 +1036,7 @@
<tabstop>mTitleAlignCombo</tabstop>
<tabstop>mMapComboBox</tabstop>
<tabstop>mWrapCharLineEdit</tabstop>
+ <tabstop>mCheckboxResizeContents</tabstop>
<tabstop>mLegendItemColGroupBox</tabstop>
<tabstop>mCheckBoxAutoUpdate</tabstop>
<tabstop>mUpdateAllPushButton</tabstop>
diff --git a/src/ui/qgscredentialdialog.ui b/src/ui/qgscredentialdialog.ui
index e3ee990..5798dcc 100644
--- a/src/ui/qgscredentialdialog.ui
+++ b/src/ui/qgscredentialdialog.ui
@@ -220,6 +220,14 @@ font-style: italic;
</item>
</layout>
</widget>
+ <tabstops>
+ <tabstop>leUsername</tabstop>
+ <tabstop>lePassword</tabstop>
+ <tabstop>leMasterPass</tabstop>
+ <tabstop>leMasterPassVerify</tabstop>
+ <tabstop>chkMasterPassShow</tabstop>
+ <tabstop>chkbxEraseAuthDb</tabstop>
+ </tabstops>
<resources/>
<connections>
<connection>
diff --git a/src/ui/qgsoptionsbase.ui b/src/ui/qgsoptionsbase.ui
index 6dcbac2..b3109fc 100644
--- a/src/ui/qgsoptionsbase.ui
+++ b/src/ui/qgsoptionsbase.ui
@@ -1408,7 +1408,7 @@
<item>
<widget class="QCheckBox" name="cbxAttributeTableDocked">
<property name="text">
- <string>Open attribute table in a dock window (QGIS restart required)</string>
+ <string>Open attribute table in a dock window</string>
</property>
</widget>
</item>
diff --git a/src/ui/qgsprojectpropertiesbase.ui b/src/ui/qgsprojectpropertiesbase.ui
index 58bc53a..f08fb0f 100644
--- a/src/ui/qgsprojectpropertiesbase.ui
+++ b/src/ui/qgsprojectpropertiesbase.ui
@@ -42,16 +42,7 @@
<enum>QFrame::Raised</enum>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
- <property name="leftMargin">
- <number>0</number>
- </property>
- <property name="topMargin">
- <number>0</number>
- </property>
- <property name="rightMargin">
- <number>0</number>
- </property>
- <property name="bottomMargin">
+ <property name="margin">
<number>0</number>
</property>
<item>
@@ -197,16 +188,7 @@
<enum>QFrame::Raised</enum>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
- <property name="leftMargin">
- <number>0</number>
- </property>
- <property name="topMargin">
- <number>0</number>
- </property>
- <property name="rightMargin">
- <number>0</number>
- </property>
- <property name="bottomMargin">
+ <property name="margin">
<number>0</number>
</property>
<item>
@@ -222,16 +204,7 @@
</property>
<widget class="QWidget" name="mProjOpts_01">
<layout class="QVBoxLayout" name="verticalLayout_6">
- <property name="leftMargin">
- <number>0</number>
- </property>
- <property name="topMargin">
- <number>0</number>
- </property>
- <property name="rightMargin">
- <number>0</number>
- </property>
- <property name="bottomMargin">
+ <property name="margin">
<number>0</number>
</property>
<item>
@@ -263,7 +236,7 @@
<property name="title">
<string>General settings</string>
</property>
- <property name="syncGroup" stdset="0">
+ <property name="syncGroup">
<string notr="true">projgeneral</string>
</property>
<layout class="QGridLayout" name="gridLayout_26">
@@ -412,22 +385,6 @@
</property>
</widget>
</item>
- <item row="0" column="1">
- <widget class="QLabel" name="projectFileName">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Maximum" vsizetype="Preferred">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="text">
- <string>Project title</string>
- </property>
- <property name="buddy">
- <cstring>titleEdit</cstring>
- </property>
- </widget>
- </item>
<item row="3" column="2" colspan="2">
<spacer>
<property name="orientation">
@@ -465,6 +422,13 @@
</property>
</widget>
</item>
+ <item row="0" column="1" colspan="3">
+ <widget class="QLineEdit" name="mProjectFileLineEdit">
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
</layout>
</widget>
</item>
@@ -473,7 +437,7 @@
<property name="title">
<string>Measurements</string>
</property>
- <property name="syncGroup" stdset="0">
+ <property name="syncGroup">
<string notr="true">projgeneral</string>
</property>
<layout class="QGridLayout" name="gridLayoutMeasureTool">
@@ -542,7 +506,7 @@
<property name="title">
<string>Coordinate display</string>
</property>
- <property name="syncGroup" stdset="0">
+ <property name="syncGroup">
<string notr="true">projgeneral</string>
</property>
<layout class="QGridLayout" name="gridLayout_18">
@@ -632,7 +596,7 @@
<property name="checked">
<bool>false</bool>
</property>
- <property name="syncGroup" stdset="0">
+ <property name="syncGroup">
<string notr="true">projgeneral</string>
</property>
<layout class="QGridLayout" name="gridLayout_7">
@@ -724,16 +688,7 @@
</widget>
<widget class="QWidget" name="mProjOpts_02">
<layout class="QVBoxLayout" name="verticalLayout_5">
- <property name="leftMargin">
- <number>0</number>
- </property>
- <property name="topMargin">
- <number>0</number>
- </property>
- <property name="rightMargin">
- <number>0</number>
- </property>
- <property name="bottomMargin">
+ <property name="margin">
<number>0</number>
</property>
<item>
@@ -783,16 +738,7 @@
</widget>
<widget class="QWidget" name="mProjOpts_03">
<layout class="QVBoxLayout" name="verticalLayout_9">
- <property name="leftMargin">
- <number>0</number>
- </property>
- <property name="topMargin">
- <number>0</number>
- </property>
- <property name="rightMargin">
- <number>0</number>
- </property>
- <property name="bottomMargin">
+ <property name="margin">
<number>0</number>
</property>
<item>
@@ -808,7 +754,7 @@
<rect>
<x>0</x>
<y>0</y>
- <width>133</width>
+ <width>134</width>
<height>100</height>
</rect>
</property>
@@ -864,16 +810,7 @@
</widget>
<widget class="QWidget" name="mProjOpts_04">
<layout class="QVBoxLayout" name="verticalLayout_11">
- <property name="leftMargin">
- <number>0</number>
- </property>
- <property name="topMargin">
- <number>0</number>
- </property>
- <property name="rightMargin">
- <number>0</number>
- </property>
- <property name="bottomMargin">
+ <property name="margin">
<number>0</number>
</property>
<item>
@@ -899,7 +836,7 @@
<property name="title">
<string>Default symbols</string>
</property>
- <property name="syncGroup" stdset="0">
+ <property name="syncGroup">
<string notr="true">projstyles</string>
</property>
<layout class="QGridLayout" name="gridLayout_11">
@@ -1114,7 +1051,7 @@
<property name="title">
<string>Options</string>
</property>
- <property name="syncGroup" stdset="0">
+ <property name="syncGroup">
<string notr="true">projstyles</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_18">
@@ -1289,16 +1226,7 @@
</widget>
<widget class="QWidget" name="mProjOpts_05">
<layout class="QVBoxLayout" name="verticalLayout_14">
- <property name="leftMargin">
- <number>0</number>
- </property>
- <property name="topMargin">
- <number>0</number>
- </property>
- <property name="rightMargin">
- <number>0</number>
- </property>
- <property name="bottomMargin">
+ <property name="margin">
<number>0</number>
</property>
<item>
@@ -1314,7 +1242,7 @@
<rect>
<x>0</x>
<y>0</y>
- <width>663</width>
+ <width>665</width>
<height>2249</height>
</rect>
</property>
@@ -1336,13 +1264,13 @@
<property name="checked">
<bool>false</bool>
</property>
- <property name="collapsed" stdset="0">
+ <property name="collapsed">
<bool>false</bool>
</property>
- <property name="saveCollapsedState" stdset="0">
+ <property name="saveCollapsedState">
<bool>true</bool>
</property>
- <property name="syncGroup" stdset="0">
+ <property name="syncGroup">
<string notr="true">projowsserver</string>
</property>
<layout class="QGridLayout" name="gridLayout_6">
@@ -1579,7 +1507,7 @@
<property name="title">
<string>WMS capabilities</string>
</property>
- <property name="syncGroup" stdset="0">
+ <property name="syncGroup">
<string notr="true">projowsserver</string>
</property>
<layout class="QGridLayout" name="gridLayout_13">
@@ -1594,10 +1522,10 @@
<property name="checked">
<bool>false</bool>
</property>
- <property name="collapsed" stdset="0">
+ <property name="collapsed">
<bool>false</bool>
</property>
- <property name="saveCollapsedState" stdset="0">
+ <property name="saveCollapsedState">
<bool>true</bool>
</property>
<layout class="QGridLayout" name="gridLayout_4">
@@ -1703,10 +1631,10 @@
<property name="checked">
<bool>false</bool>
</property>
- <property name="collapsed" stdset="0">
+ <property name="collapsed">
<bool>false</bool>
</property>
- <property name="saveCollapsedState" stdset="0">
+ <property name="saveCollapsedState">
<bool>true</bool>
</property>
<layout class="QGridLayout" name="gridLayout_10">
@@ -1762,10 +1690,10 @@
<property name="checked">
<bool>false</bool>
</property>
- <property name="collapsed" stdset="0">
+ <property name="collapsed">
<bool>false</bool>
</property>
- <property name="saveCollapsedState" stdset="0">
+ <property name="saveCollapsedState">
<bool>true</bool>
</property>
<layout class="QGridLayout" name="gridLayout">
@@ -1821,10 +1749,10 @@
<property name="checked">
<bool>false</bool>
</property>
- <property name="collapsed" stdset="0">
+ <property name="collapsed">
<bool>false</bool>
</property>
- <property name="saveCollapsedState" stdset="0">
+ <property name="saveCollapsedState">
<bool>true</bool>
</property>
<layout class="QGridLayout" name="gridLayout_5">
@@ -2129,7 +2057,7 @@
<property name="title">
<string>WFS capabilities (also influences DXF export)</string>
</property>
- <property name="syncGroup" stdset="0">
+ <property name="syncGroup">
<string notr="true">projowsserver</string>
</property>
<layout class="QGridLayout" name="gridLayout_8">
@@ -2215,7 +2143,7 @@
<property name="title">
<string>WCS capabilities</string>
</property>
- <property name="syncGroup" stdset="0">
+ <property name="syncGroup">
<string notr="true">projowsserver</string>
</property>
<layout class="QGridLayout" name="gridLayout_9">
@@ -2355,16 +2283,7 @@
</widget>
<widget class="QWidget" name="mProjOpts_06">
<layout class="QVBoxLayout" name="verticalLayout_15">
- <property name="leftMargin">
- <number>0</number>
- </property>
- <property name="topMargin">
- <number>0</number>
- </property>
- <property name="rightMargin">
- <number>0</number>
- </property>
- <property name="bottomMargin">
+ <property name="margin">
<number>0</number>
</property>
<item>
@@ -2417,16 +2336,7 @@
</widget>
<widget class="QWidget" name="mTabRelations">
<layout class="QGridLayout" name="gridLayout_16">
- <property name="leftMargin">
- <number>0</number>
- </property>
- <property name="topMargin">
- <number>0</number>
- </property>
- <property name="rightMargin">
- <number>0</number>
- </property>
- <property name="bottomMargin">
+ <property name="margin">
<number>0</number>
</property>
</layout>
@@ -2472,16 +2382,7 @@
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
- <property name="leftMargin">
- <number>0</number>
- </property>
- <property name="topMargin">
- <number>0</number>
- </property>
- <property name="rightMargin">
- <number>0</number>
- </property>
- <property name="bottomMargin">
+ <property name="margin">
<number>0</number>
</property>
<item>
@@ -2540,6 +2441,7 @@
<tabstops>
<tabstop>mOptionsListWidget</tabstop>
<tabstop>scrollArea_2</tabstop>
+ <tabstop>mProjectFileLineEdit</tabstop>
<tabstop>titleEdit</tabstop>
<tabstop>pbnSelectionColor</tabstop>
<tabstop>pbnCanvasColor</tabstop>
diff --git a/tests/src/analysis/testqgsalignraster.cpp b/tests/src/analysis/testqgsalignraster.cpp
index ba62980..dbeb79b 100644
--- a/tests/src/analysis/testqgsalignraster.cpp
+++ b/tests/src/analysis/testqgsalignraster.cpp
@@ -169,7 +169,7 @@ class TestAlignRaster : public QObject
QgsAlignRaster align;
QgsAlignRaster::List rasters;
rasters << QgsAlignRaster::Item( SRC_FILE, tmpFile );
- rasters[0].resampleMethod = QgsAlignRaster::RA_Bilinear;
+ rasters[0].resampleMethod = QgsAlignRaster::RA_Average;
align.setRasters( rasters );
align.setParametersFromRaster( SRC_FILE, QString(), QSizeF( 0.4, 0.4 ) );
bool res = align.run();
@@ -190,7 +190,7 @@ class TestAlignRaster : public QObject
QgsAlignRaster align;
QgsAlignRaster::List rasters;
rasters << QgsAlignRaster::Item( SRC_FILE, tmpFile );
- rasters[0].resampleMethod = QgsAlignRaster::RA_Bilinear;
+ rasters[0].resampleMethod = QgsAlignRaster::RA_Average;
rasters[0].rescaleValues = true;
align.setRasters( rasters );
align.setParametersFromRaster( SRC_FILE, QString(), QSizeF( 0.4, 0.4 ) );
diff --git a/tests/src/core/testqgsapplication.cpp b/tests/src/core/testqgsapplication.cpp
index ab74259..35a1275 100644
--- a/tests/src/core/testqgsapplication.cpp
+++ b/tests/src/core/testqgsapplication.cpp
@@ -83,6 +83,7 @@ void TestQgsApplication::platformName()
QCOMPARE( QgsApplication::platform(), QString( "desktop" ) );
}
+
void TestQgsApplication::checkPaths()
{
QString myPath = QgsApplication::authorsFilePath();
diff --git a/tests/src/core/testqgscomposition.cpp b/tests/src/core/testqgscomposition.cpp
index f4041d2..3af8b1a 100644
--- a/tests/src/core/testqgscomposition.cpp
+++ b/tests/src/core/testqgscomposition.cpp
@@ -51,6 +51,7 @@ class TestQgsComposition : public QObject
void resizeToContents();
void resizeToContentsMargin();
void resizeToContentsMultiPage();
+ void variablesEdited();
private:
QgsComposition *mComposition;
@@ -511,5 +512,19 @@ void TestQgsComposition::resizeToContentsMultiPage()
delete composition;
}
+void TestQgsComposition::variablesEdited()
+{
+ QgsMapSettings ms;
+ QgsComposition c( ms );
+ QSignalSpy spyVariablesChanged( &c, SIGNAL( variablesChanged() ) );
+
+ c.setCustomProperty( "not a variable", "1" );
+ QVERIFY( spyVariablesChanged.count() == 0 );
+ c.setCustomProperty( "variableNames", "1" );
+ QVERIFY( spyVariablesChanged.count() == 1 );
+ c.setCustomProperty( "variableValues", "1" );
+ QVERIFY( spyVariablesChanged.count() == 2 );
+}
+
QTEST_MAIN( TestQgsComposition )
#include "testqgscomposition.moc"
diff --git a/tests/src/core/testqgscoordinatereferencesystem.cpp b/tests/src/core/testqgscoordinatereferencesystem.cpp
index afa1c02..37b3b79 100644
--- a/tests/src/core/testqgscoordinatereferencesystem.cpp
+++ b/tests/src/core/testqgscoordinatereferencesystem.cpp
@@ -214,8 +214,14 @@ void TestQgsCoordinateReferenceSystem::createFromESRIWkt()
myWktStrings << "GEOGCS[\"GCS_South_American_1969\",DATUM[\"D_South_American_1969\",SPHEROID[\"GRS_1967_Truncated\",6378160.0,298.25]],PRIMEM[\"Greenwich\",0.0],UNIT[\"Degree\",0.0174532925199433]]";
myFiles << "";
myGdalVersionOK << 1900;
+#if defined(GDAL_VERSION_NUM) && GDAL_VERSION_NUM >= 2000000
+ //proj definition for EPSG:4618 was updated in GDAL 2.0 - see https://github.com/OSGeo/proj.4/issues/241
+ myProj4Strings << "+proj=longlat +ellps=aust_SA +towgs84=-66.87,4.37,-38.52,0,0,0,0 +no_defs";
+ myTOWGS84Strings << "+towgs84=-66.87,4.37,-38.52,0,0,0,0";
+#else
myProj4Strings << "+proj=longlat +ellps=aust_SA +towgs84=-57,1,-41,0,0,0,0 +no_defs";
myTOWGS84Strings << "+towgs84=-57,1,-41,0,0,0,0";
+#endif
myAuthIdStrings << "EPSG:4618";
// do test with WKT definitions
diff --git a/tests/src/core/testqgsdistancearea.cpp b/tests/src/core/testqgsdistancearea.cpp
index 02f337c..3e4c9d7 100644
--- a/tests/src/core/testqgsdistancearea.cpp
+++ b/tests/src/core/testqgsdistancearea.cpp
@@ -365,7 +365,8 @@ void TestQgsDistanceArea::regression14675()
calc.setEllipsoid( "GRS80" );
calc.setSourceCrs( 145L );
QgsGeometry geom( QgsGeometryFactory::geomFromWkt( "Polygon ((917593.5791854317067191 6833700.00807378999888897, 917596.43389983859378844 6833700.67099479306489229, 917599.53056440979707986 6833700.78673478215932846, 917593.5791854317067191 6833700.00807378999888897))" ) );
- QVERIFY( qgsDoubleNear( calc.measureArea( &geom ), 0.83301, 0.0001 ) );
+ //lots of tolerance here - the formulas get quite unstable with small areas due to division by very small floats
+ QVERIFY( qgsDoubleNear( calc.measureArea( &geom ), 0.83301, 0.02 ) );
}
QTEST_MAIN( TestQgsDistanceArea )
diff --git a/tests/src/core/testqgsexpressioncontext.cpp b/tests/src/core/testqgsexpressioncontext.cpp
index 9bcc666..c62e857 100644
--- a/tests/src/core/testqgsexpressioncontext.cpp
+++ b/tests/src/core/testqgsexpressioncontext.cpp
@@ -37,6 +37,7 @@ class TestQgsExpressionContext : public QObject
void contextScopeCopy();
void contextScopeFunctions();
void contextStack();
+ void scopeByName();
void contextCopy();
void contextStackFunctions();
void evaluate();
@@ -300,6 +301,17 @@ void TestQgsExpressionContext::contextStack()
QCOMPARE( scopes.at( 0 ), scope1 );
}
+void TestQgsExpressionContext::scopeByName()
+{
+ QgsExpressionContext context;
+ QCOMPARE( context.indexOfScope( "test1" ), -1 );
+ context << new QgsExpressionContextScope( "test1" );
+ context << new QgsExpressionContextScope( "test2" );
+ QCOMPARE( context.indexOfScope( "test1" ), 0 );
+ QCOMPARE( context.indexOfScope( "test2" ), 1 );
+ QCOMPARE( context.indexOfScope( "not in context" ), -1 );
+}
+
void TestQgsExpressionContext::contextCopy()
{
QgsExpressionContext context;
diff --git a/tests/src/core/testqgsgeometry.cpp b/tests/src/core/testqgsgeometry.cpp
index 8fe4cdd..785c4ab 100644
--- a/tests/src/core/testqgsgeometry.cpp
+++ b/tests/src/core/testqgsgeometry.cpp
@@ -88,6 +88,8 @@ class TestQgsGeometry : public QObject
void bufferCheck();
void smoothCheck();
+ void unaryUnion();
+
void dataStream();
void exportToGeoJSON();
@@ -3240,6 +3242,20 @@ void TestQgsGeometry::smoothCheck()
QVERIFY( QgsGeometry::compare( multipoly, expectedMultiPoly ) );
}
+void TestQgsGeometry::unaryUnion()
+{
+ //test QgsGeometry::unaryUnion with null geometry
+ QString wkt1 = "Polygon ((0 0, 10 0, 10 10, 0 10, 0 0 ))";
+ QString wkt2 = "Polygon ((2 2, 4 2, 4 4, 2 4, 2 2))";
+ QScopedPointer< QgsGeometry > geom1( QgsGeometry::fromWkt( wkt1 ) );
+ QScopedPointer< QgsGeometry > geom2( QgsGeometry::fromWkt( wkt2 ) );
+ QScopedPointer< QgsGeometry > empty( new QgsGeometry() );
+ QList< QgsGeometry* > list;
+ list << geom1.data() << empty.data() << geom2.data();
+
+ QScopedPointer< QgsGeometry > result( QgsGeometry::unaryUnion( list ) );
+}
+
void TestQgsGeometry::dataStream()
{
QString wkt = "Point (40 50)";
diff --git a/tests/src/core/testqgsproject.cpp b/tests/src/core/testqgsproject.cpp
index d8a7414..7a8b3c7 100644
--- a/tests/src/core/testqgsproject.cpp
+++ b/tests/src/core/testqgsproject.cpp
@@ -31,6 +31,7 @@ class TestQgsProject : public QObject
void testReadPath();
void testProjectUnits();
+ void variablesChanged();
};
void TestQgsProject::init()
@@ -124,6 +125,13 @@ void TestQgsProject::testProjectUnits()
QCOMPARE( prj->areaUnits(), QgsUnitTypes::Acres );
}
+void TestQgsProject::variablesChanged()
+{
+ QSignalSpy spyVariablesChanged( QgsProject::instance(), SIGNAL( variablesChanged() ) );
+ QgsProject::instance()->emitVariablesChanged();
+ QVERIFY( spyVariablesChanged.count() == 1 );
+}
+
QTEST_MAIN( TestQgsProject )
#include "testqgsproject.moc"
diff --git a/tests/src/core/testqgsrasterlayer.cpp b/tests/src/core/testqgsrasterlayer.cpp
index 77efc58..5cf9326 100644
--- a/tests/src/core/testqgsrasterlayer.cpp
+++ b/tests/src/core/testqgsrasterlayer.cpp
@@ -372,12 +372,11 @@ void TestQgsRasterLayer::checkStats()
//QVERIFY( myStatistics.elementCount == 100 );
QVERIFY( myStatistics.minimumValue == 0 );
QVERIFY( myStatistics.maximumValue == 9 );
- QVERIFY( myStatistics.mean == 4.5 );
+ QVERIFY( qgsDoubleNear( myStatistics.mean, 4.5 ) );
double stdDev = 2.87228132326901431;
// TODO: verify why GDAL stdDev is so different from generic (2.88675)
mReport += QString( "stdDev = %1 expected = %2<br>\n" ).arg( myStatistics.stdDev ).arg( stdDev );
- QVERIFY( fabs( myStatistics.stdDev - stdDev )
- < 0.0000000000000001 );
+ QVERIFY( qgsDoubleNear( myStatistics.stdDev, stdDev, 0.00000000000001 ) );
mReport += "<p>Passed</p>";
}
diff --git a/tests/src/providers/CMakeLists.txt b/tests/src/providers/CMakeLists.txt
index badad73..7e8b2e9 100644
--- a/tests/src/providers/CMakeLists.txt
+++ b/tests/src/providers/CMakeLists.txt
@@ -9,6 +9,7 @@ INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_SOURCE_DIR}/src/core/auth
${CMAKE_SOURCE_DIR}/src/core/geometry
${CMAKE_SOURCE_DIR}/src/core/raster
+ ${CMAKE_SOURCE_DIR}/src/providers/wms
)
INCLUDE_DIRECTORIES(SYSTEM
${QT_INCLUDE_DIR}
@@ -84,6 +85,14 @@ SET_TARGET_PROPERTIES(qgis_wcsprovidertest PROPERTIES
ADD_QGIS_TEST(gdalprovidertest testqgsgdalprovider.cpp)
+ADD_QGIS_TEST(wmscapabilititestest
+ testqgswmscapabilities.cpp)
+TARGET_LINK_LIBRARIES(qgis_wmscapabilititestest wmsprovider_a)
+
+ADD_QGIS_TEST(wmsprovidertest
+ testqgswmsprovider.cpp)
+TARGET_LINK_LIBRARIES(qgis_wmsprovidertest wmsprovider_a)
+
#############################################################
# WCS public servers test:
# No need to test on all platforms
diff --git a/tests/src/providers/testqgswmscapabilities.cpp b/tests/src/providers/testqgswmscapabilities.cpp
new file mode 100644
index 0000000..686c30a
--- /dev/null
+++ b/tests/src/providers/testqgswmscapabilities.cpp
@@ -0,0 +1,70 @@
+#include <QFile>
+#include <QObject>
+#include <QtTest/QtTest>
+#include <qgswmscapabilities.h>
+#include <qgsapplication.h>
+
+/** \ingroup UnitTests
+ * This is a unit test for the WMS capabilities parser.
+ */
+class TestQgsWmsCapabilities: public QObject
+{
+ Q_OBJECT
+ private slots:
+
+ void initTestCase()
+ {
+ // init QGIS's paths - true means that all path will be inited from prefix
+ QgsApplication::init();
+ QgsApplication::initQgis();
+ }
+
+ //runs after all tests
+ void cleanupTestCase()
+ {
+ QgsApplication::exitQgis();
+ }
+
+
+ void read()
+ {
+ QgsWmsCapabilities capabilities;
+
+ QFile file( QString( TEST_DATA_DIR ) + "/provider/GetCapabilities.xml" );
+ QVERIFY( file.open( QIODevice::ReadOnly | QIODevice::Text ) );
+ const QByteArray content = file.readAll();
+ QVERIFY( content.size() > 0 );
+ const QgsWmsParserSettings config;
+
+ QVERIFY( capabilities.parseResponse( content, config ) );
+ QCOMPARE( capabilities.supportedLayers().size(), 5 );
+ QCOMPARE( capabilities.supportedLayers()[0].name, QString( "agri_zones" ) );
+ QCOMPARE( capabilities.supportedLayers()[1].name, QString( "buildings" ) );
+ QCOMPARE( capabilities.supportedLayers()[2].name, QString( "land_surveing_parcels" ) );
+ QCOMPARE( capabilities.supportedLayers()[3].name, QString( "cadastre" ) );
+ QCOMPARE( capabilities.supportedLayers()[4].name, QString( "test" ) );
+
+ // make sure the default style is not seen twice in the child layers
+ QCOMPARE( capabilities.supportedLayers()[3].style.size(), 1 );
+ QCOMPARE( capabilities.supportedLayers()[3].style[0].name, QString( "default" ) );
+ QCOMPARE( capabilities.supportedLayers()[1].style.size(), 1 );
+ QCOMPARE( capabilities.supportedLayers()[1].style[0].name, QString( "default" ) );
+ QCOMPARE( capabilities.supportedLayers()[2].style.size(), 1 );
+ QCOMPARE( capabilities.supportedLayers()[2].style[0].name, QString( "default" ) );
+
+ // check it can read 2 styles for a layer and that the legend URL is OK
+ QCOMPARE( capabilities.supportedLayers()[0].style.size(), 2 );
+ QCOMPARE( capabilities.supportedLayers()[0].style[0].name, QString( "yt_style" ) );
+ QCOMPARE( capabilities.supportedLayers()[0].style[0].legendUrl.size(), 1 );
+ QCOMPARE( capabilities.supportedLayers()[0].style[0].legendUrl[0].onlineResource.xlinkHref,
+ QString( "http://www.example.com/yt.png" ) );
+ QCOMPARE( capabilities.supportedLayers()[0].style[1].name, QString( "fb_style" ) );
+ QCOMPARE( capabilities.supportedLayers()[0].style[1].legendUrl.size(), 1 );
+ QCOMPARE( capabilities.supportedLayers()[0].style[1].legendUrl[0].onlineResource.xlinkHref,
+ QString( "http://www.example.com/fb.png" ) );
+ }
+
+};
+
+QTEST_MAIN( TestQgsWmsCapabilities )
+#include "testqgswmscapabilities.moc"
diff --git a/tests/src/providers/testqgswmsprovider.cpp b/tests/src/providers/testqgswmsprovider.cpp
new file mode 100644
index 0000000..1f55efd
--- /dev/null
+++ b/tests/src/providers/testqgswmsprovider.cpp
@@ -0,0 +1,69 @@
+#include <QFile>
+#include <QObject>
+#include <QtTest/QtTest>
+#include <qgswmsprovider.h>
+#include <qgsapplication.h>
+
+/** \ingroup UnitTests
+ * This is a unit test for the WMS provider.
+ */
+class TestQgsWmsProvider: public QObject
+{
+ Q_OBJECT
+ private slots:
+
+ void initTestCase()
+ {
+ // init QGIS's paths - true means that all path will be inited from prefix
+ QgsApplication::init();
+ QgsApplication::initQgis();
+
+ QFile file( QString( TEST_DATA_DIR ) + "/provider/GetCapabilities.xml" );
+ QVERIFY( file.open( QIODevice::ReadOnly | QIODevice::Text ) );
+ const QByteArray content = file.readAll();
+ QVERIFY( content.size() > 0 );
+ const QgsWmsParserSettings config;
+
+ mCapabilities = new QgsWmsCapabilities();
+ QVERIFY( mCapabilities->parseResponse( content, config ) );
+ }
+
+ //runs after all tests
+ void cleanupTestCase()
+ {
+ delete mCapabilities;
+ QgsApplication::exitQgis();
+ }
+
+ void legendGraphicsWithStyle()
+ {
+ QgsWmsProvider provider( "http://localhost:8380/mapserv?xxx&layers=agri_zones&styles=fb_style&format=image/jpg", mCapabilities );
+ QCOMPARE( provider.getLegendGraphicUrl(), QString( "http://www.example.com/fb.png?" ) );
+ }
+
+ void legendGraphicsWithSecondStyle()
+ {
+ QgsWmsProvider provider( "http://localhost:8380/mapserv?xxx&layers=agri_zones&styles=yt_style&format=image/jpg", mCapabilities );
+ QCOMPARE( provider.getLegendGraphicUrl(), QString( "http://www.example.com/yt.png?" ) );
+ }
+
+ void legendGraphicsWithoutStyleWithDefault()
+ {
+ QgsWmsProvider provider( "http://localhost:8380/mapserv?xxx&layers=buildings&styles=&format=image/jpg", mCapabilities );
+ //only one style, can guess default => use it
+ QCOMPARE( provider.getLegendGraphicUrl(), QString( "http://www.example.com/buildings.png?" ) );
+ }
+
+ void legendGraphicsWithoutStyleWithoutDefault()
+ {
+ QgsWmsProvider provider( "http://localhost:8380/mapserv?xxx&layers=agri_zones&styles=&format=image/jpg", mCapabilities );
+ //two style, cannot guess default => use the WMS GetLegendGraphics
+ QCOMPARE( provider.getLegendGraphicUrl(), QString( "http://localhost:8380/mapserv?" ) );
+ }
+
+ private:
+ QgsWmsCapabilities* mCapabilities;
+};
+
+QTEST_MAIN( TestQgsWmsProvider )
+#include "testqgswmsprovider.moc"
diff --git a/tests/src/python/CMakeLists.txt b/tests/src/python/CMakeLists.txt
index 38ab9e9..3dbd0e1 100644
--- a/tests/src/python/CMakeLists.txt
+++ b/tests/src/python/CMakeLists.txt
@@ -21,6 +21,7 @@ ADD_PYTHON_TEST(PyQgsColorSchemeRegistry test_qgscolorschemeregistry.py)
ADD_PYTHON_TEST(PyQgsComposerEffects test_qgscomposereffects.py)
ADD_PYTHON_TEST(PyQgsComposerHtml test_qgscomposerhtml.py)
ADD_PYTHON_TEST(PyQgsComposerLabel test_qgscomposerlabel.py)
+ADD_PYTHON_TEST(PyQgsComposerLegend test_qgscomposerlegend.py)
ADD_PYTHON_TEST(PyQgsComposerMap test_qgscomposermap.py)
ADD_PYTHON_TEST(PyQgsComposerMapGrid test_qgscomposermapgrid.py)
ADD_PYTHON_TEST(PyQgsComposerPicture test_qgscomposerpicture.py)
diff --git a/tests/src/python/test_qgscomposerlegend.py b/tests/src/python/test_qgscomposerlegend.py
new file mode 100644
index 0000000..dd3e9c1
--- /dev/null
+++ b/tests/src/python/test_qgscomposerlegend.py
@@ -0,0 +1,203 @@
+# -*- coding: utf-8 -*-
+"""QGIS Unit tests for QgsComposerLegend.
+
+.. note:: 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.
+"""
+__author__ = '(C) 2016 by Nyall Dawson'
+__date__ = '13/07/2016'
+__copyright__ = 'Copyright 2016, The QGIS Project'
+# This will get replaced with a git SHA1 when you do a git archive
+__revision__ = '$Format:%H$'
+
+from qgis.PyQt.QtCore import QRectF
+from qgis.PyQt.QtGui import QColor
+
+from qgis.core import (QgsComposerLegend,
+ QgsComposerMap,
+ QgsComposition,
+ QgsMapSettings,
+ QgsVectorLayer,
+ QgsMapLayerRegistry,
+ QgsMarkerSymbolV2,
+ QgsSingleSymbolRendererV2,
+ QgsRectangle
+ )
+from qgis.testing import (start_app,
+ unittest
+ )
+from utilities import unitTestDataPath
+from qgscompositionchecker import QgsCompositionChecker
+import os
+
+start_app()
+TEST_DATA_DIR = unitTestDataPath()
+
+
+class TestQgsComposerLegend(unittest.TestCase):
+
+ def testInitialSizeSymbolMapUnits(self):
+ """Test initial size of legend with a symbol size in map units"""
+
+ point_path = os.path.join(TEST_DATA_DIR, 'points.shp')
+ point_layer = QgsVectorLayer(point_path, 'points', 'ogr')
+ QgsMapLayerRegistry.instance().addMapLayers([point_layer])
+
+ marker_symbol = QgsMarkerSymbolV2.createSimple({'color': '#ff0000', 'outline_style': 'no', 'size': '5', 'size_unit': 'MapUnit'})
+
+ point_layer.setRendererV2(QgsSingleSymbolRendererV2(marker_symbol))
+
+ s = QgsMapSettings()
+ s.setLayers([point_layer.id()])
+ s.setCrsTransformEnabled(False)
+ composition = QgsComposition(s)
+ composition.setPaperSize(297, 210)
+
+ composer_map = QgsComposerMap(composition, 20, 20, 80, 80)
+ composer_map.setFrameEnabled(True)
+ composition.addComposerMap(composer_map)
+ composer_map.setNewExtent(point_layer.extent())
+
+ legend = QgsComposerLegend(composition)
+ legend.setSceneRect(QRectF(120, 20, 80, 80))
+ legend.setFrameEnabled(True)
+ legend.setFrameOutlineWidth(2)
+ legend.setBackgroundColor(QColor(200, 200, 200))
+ legend.setTitle('')
+ composition.addComposerLegend(legend)
+ legend.setComposerMap(composer_map)
+
+ checker = QgsCompositionChecker(
+ 'composer_legend_mapunits', composition)
+ checker.setControlPathPrefix("composer_legend")
+ result, message = checker.testComposition()
+ self.assertTrue(result, message)
+
+ QgsMapLayerRegistry.instance().removeMapLayers([point_layer])
+
+ def testResizeWithMapContent(self):
+ """Test test legend resizes to match map content"""
+
+ point_path = os.path.join(TEST_DATA_DIR, 'points.shp')
+ point_layer = QgsVectorLayer(point_path, 'points', 'ogr')
+ QgsMapLayerRegistry.instance().addMapLayers([point_layer])
+
+ s = QgsMapSettings()
+ s.setLayers([point_layer.id()])
+ s.setCrsTransformEnabled(False)
+ composition = QgsComposition(s)
+ composition.setPaperSize(297, 210)
+
+ composer_map = QgsComposerMap(composition, 20, 20, 80, 80)
+ composer_map.setFrameEnabled(True)
+ composition.addComposerMap(composer_map)
+ composer_map.setNewExtent(point_layer.extent())
+
+ legend = QgsComposerLegend(composition)
+ legend.setSceneRect(QRectF(120, 20, 80, 80))
+ legend.setFrameEnabled(True)
+ legend.setFrameOutlineWidth(2)
+ legend.setBackgroundColor(QColor(200, 200, 200))
+ legend.setTitle('')
+ legend.setLegendFilterByMapEnabled(True)
+ composition.addComposerLegend(legend)
+ legend.setComposerMap(composer_map)
+
+ composer_map.setNewExtent(QgsRectangle(-102.51, 41.16, -102.36, 41.30))
+
+ checker = QgsCompositionChecker(
+ 'composer_legend_size_content', composition)
+ checker.setControlPathPrefix("composer_legend")
+ result, message = checker.testComposition()
+ self.assertTrue(result, message)
+
+ QgsMapLayerRegistry.instance().removeMapLayers([point_layer])
+
+ def testResizeDisabled(self):
+ """Test that test legend does not resize if auto size is disabled"""
+
+ point_path = os.path.join(TEST_DATA_DIR, 'points.shp')
+ point_layer = QgsVectorLayer(point_path, 'points', 'ogr')
+ QgsMapLayerRegistry.instance().addMapLayers([point_layer])
+
+ s = QgsMapSettings()
+ s.setLayers([point_layer.id()])
+ s.setCrsTransformEnabled(False)
+ composition = QgsComposition(s)
+ composition.setPaperSize(297, 210)
+
+ composer_map = QgsComposerMap(composition, 20, 20, 80, 80)
+ composer_map.setFrameEnabled(True)
+ composition.addComposerMap(composer_map)
+ composer_map.setNewExtent(point_layer.extent())
+
+ legend = QgsComposerLegend(composition)
+ legend.setSceneRect(QRectF(120, 20, 80, 80))
+ legend.setFrameEnabled(True)
+ legend.setFrameOutlineWidth(2)
+ legend.setBackgroundColor(QColor(200, 200, 200))
+ legend.setTitle('')
+ legend.setLegendFilterByMapEnabled(True)
+
+ #disable auto resizing
+ legend.setResizeToContents(False)
+
+ composition.addComposerLegend(legend)
+ legend.setComposerMap(composer_map)
+
+ composer_map.setNewExtent(QgsRectangle(-102.51, 41.16, -102.36, 41.30))
+
+ checker = QgsCompositionChecker(
+ 'composer_legend_noresize', composition)
+ checker.setControlPathPrefix("composer_legend")
+ result, message = checker.testComposition()
+ self.assertTrue(result, message)
+
+ QgsMapLayerRegistry.instance().removeMapLayers([point_layer])
+
+ def testResizeDisabledCrop(self):
+ """Test that if legend resizing is disabled, and legend is too small, then content is cropped"""
+
+ point_path = os.path.join(TEST_DATA_DIR, 'points.shp')
+ point_layer = QgsVectorLayer(point_path, 'points', 'ogr')
+ QgsMapLayerRegistry.instance().addMapLayers([point_layer])
+
+ s = QgsMapSettings()
+ s.setLayers([point_layer.id()])
+ s.setCrsTransformEnabled(False)
+ composition = QgsComposition(s)
+ composition.setPaperSize(297, 210)
+
+ composer_map = QgsComposerMap(composition, 20, 20, 80, 80)
+ composer_map.setFrameEnabled(True)
+ composition.addComposerMap(composer_map)
+ composer_map.setNewExtent(point_layer.extent())
+
+ legend = QgsComposerLegend(composition)
+ legend.setSceneRect(QRectF(120, 20, 20, 20))
+ legend.setFrameEnabled(True)
+ legend.setFrameOutlineWidth(2)
+ legend.setBackgroundColor(QColor(200, 200, 200))
+ legend.setTitle('')
+ legend.setLegendFilterByMapEnabled(True)
+
+ # disable auto resizing
+ legend.setResizeToContents(False)
+
+ composition.addComposerLegend(legend)
+ legend.setComposerMap(composer_map)
+
+ composer_map.setNewExtent(QgsRectangle(-102.51, 41.16, -102.36, 41.30))
+
+ checker = QgsCompositionChecker(
+ 'composer_legend_noresize_crop', composition)
+ checker.setControlPathPrefix("composer_legend")
+ result, message = checker.testComposition()
+ self.assertTrue(result, message)
+
+ QgsMapLayerRegistry.instance().removeMapLayers([point_layer])
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/tests/src/python/test_qgspallabeling_tests.py b/tests/src/python/test_qgspallabeling_tests.py
index 2fa9978..a70a740 100644
--- a/tests/src/python/test_qgspallabeling_tests.py
+++ b/tests/src/python/test_qgspallabeling_tests.py
@@ -184,6 +184,22 @@ class TestPointBase(object):
self.lyr.shadowTransparency = 0
self.checkTest()
+ def test_letter_spacing(self):
+ # Modified letter spacing
+ font = QFont(self._TestFont)
+ font.setLetterSpacing(QFont.AbsoluteSpacing, 3.5)
+ font.setPointSizeF(30)
+ self.lyr.textFont = font
+ self.checkTest()
+
+ def test_word_spacing(self):
+ # Modified word spacing
+ font = QFont(self._TestFont)
+ font.setPointSizeF(30)
+ font.setWordSpacing(20.5)
+ self.lyr.textFont = font
+ self.checkTest()
+
# noinspection PyPep8Naming
diff --git a/tests/src/python/utilities.py b/tests/src/python/utilities.py
index 14930a7..b6faa51 100644
--- a/tests/src/python/utilities.py
+++ b/tests/src/python/utilities.py
@@ -453,6 +453,7 @@ class DoxygenParser():
for m in e.getiterator('memberdef'):
if self.elemIsBindableMember(m):
bindable_member = [e.find('compoundname').text, m.find('name').text]
+
if not bindable_member in bindable_members:
bindable_members.append(bindable_member)
if self.elemIsDocumentableMember(m):
@@ -481,6 +482,17 @@ class DoxygenParser():
:param elem: XML element for a class member
"""
+ name = elem.find('name').text
+ # hack to work around doxygen mistakenly flagging some private members as public
+ if name in ['runBlockOperationInThreads',
+ 'runLineOperation',
+ 'runLineOperationOnWholeImage',
+ 'runPixelOperation',
+ 'runPixelOperationOnWholeImage',
+ 'runRectOperation',
+ 'runRectOperationOnWholeImage']:
+ return False
+
# only public or protected members are bindable
if not self.visibility(elem) in ('public', 'protected'):
return False
diff --git a/tests/testdata/control_images/composer_legend/expected_composer_legend_mapunits/expected_composer_legend_mapunits.png b/tests/testdata/control_images/composer_legend/expected_composer_legend_mapunits/expected_composer_legend_mapunits.png
new file mode 100644
index 0000000..b06acdf
Binary files /dev/null and b/tests/testdata/control_images/composer_legend/expected_composer_legend_mapunits/expected_composer_legend_mapunits.png differ
diff --git a/tests/testdata/control_images/composer_legend/expected_composer_legend_mapunits/expected_composer_legend_mapunits_mask.png b/tests/testdata/control_images/composer_legend/expected_composer_legend_mapunits/expected_composer_legend_mapunits_mask.png
new file mode 100644
index 0000000..b889494
Binary files /dev/null and b/tests/testdata/control_images/composer_legend/expected_composer_legend_mapunits/expected_composer_legend_mapunits_mask.png differ
diff --git a/tests/testdata/control_images/composer_legend/expected_composer_legend_noresize/expected_composer_legend_noresize.png b/tests/testdata/control_images/composer_legend/expected_composer_legend_noresize/expected_composer_legend_noresize.png
new file mode 100644
index 0000000..b157219
Binary files /dev/null and b/tests/testdata/control_images/composer_legend/expected_composer_legend_noresize/expected_composer_legend_noresize.png differ
diff --git a/tests/testdata/control_images/composer_legend/expected_composer_legend_noresize/expected_composer_legend_noresize_mask.png b/tests/testdata/control_images/composer_legend/expected_composer_legend_noresize/expected_composer_legend_noresize_mask.png
new file mode 100644
index 0000000..6fd8107
Binary files /dev/null and b/tests/testdata/control_images/composer_legend/expected_composer_legend_noresize/expected_composer_legend_noresize_mask.png differ
diff --git a/tests/testdata/control_images/composer_legend/expected_composer_legend_noresize_crop/expected_composer_legend_noresize_crop.png b/tests/testdata/control_images/composer_legend/expected_composer_legend_noresize_crop/expected_composer_legend_noresize_crop.png
new file mode 100644
index 0000000..1f43f67
Binary files /dev/null and b/tests/testdata/control_images/composer_legend/expected_composer_legend_noresize_crop/expected_composer_legend_noresize_crop.png differ
diff --git a/tests/testdata/control_images/composer_legend/expected_composer_legend_noresize_crop/expected_composer_legend_noresize_crop_mask.png b/tests/testdata/control_images/composer_legend/expected_composer_legend_noresize_crop/expected_composer_legend_noresize_crop_mask.png
new file mode 100644
index 0000000..9950334
Binary files /dev/null and b/tests/testdata/control_images/composer_legend/expected_composer_legend_noresize_crop/expected_composer_legend_noresize_crop_mask.png differ
diff --git a/tests/testdata/control_images/composer_legend/expected_composer_legend_size_content/expected_composer_legend_size_content.png b/tests/testdata/control_images/composer_legend/expected_composer_legend_size_content/expected_composer_legend_size_content.png
new file mode 100644
index 0000000..604dbbc
Binary files /dev/null and b/tests/testdata/control_images/composer_legend/expected_composer_legend_size_content/expected_composer_legend_size_content.png differ
diff --git a/tests/testdata/control_images/composer_legend/expected_composer_legend_size_content/expected_composer_legend_size_content_mask.png b/tests/testdata/control_images/composer_legend/expected_composer_legend_size_content/expected_composer_legend_size_content_mask.png
new file mode 100644
index 0000000..98e4b26
Binary files /dev/null and b/tests/testdata/control_images/composer_legend/expected_composer_legend_size_content/expected_composer_legend_size_content_mask.png differ
diff --git a/tests/testdata/control_images/expected_pal_canvas/sp_letter_spacing/sp_letter_spacing.png b/tests/testdata/control_images/expected_pal_canvas/sp_letter_spacing/sp_letter_spacing.png
new file mode 100644
index 0000000..5304b16
Binary files /dev/null and b/tests/testdata/control_images/expected_pal_canvas/sp_letter_spacing/sp_letter_spacing.png differ
diff --git a/tests/testdata/control_images/expected_pal_canvas/sp_letter_spacing/sp_letter_spacing_mask.png b/tests/testdata/control_images/expected_pal_canvas/sp_letter_spacing/sp_letter_spacing_mask.png
new file mode 100644
index 0000000..c3493bd
Binary files /dev/null and b/tests/testdata/control_images/expected_pal_canvas/sp_letter_spacing/sp_letter_spacing_mask.png differ
diff --git a/tests/testdata/control_images/expected_pal_canvas/sp_word_spacing/sp_word_spacing.png b/tests/testdata/control_images/expected_pal_canvas/sp_word_spacing/sp_word_spacing.png
new file mode 100644
index 0000000..7c40ca0
Binary files /dev/null and b/tests/testdata/control_images/expected_pal_canvas/sp_word_spacing/sp_word_spacing.png differ
diff --git a/tests/testdata/control_images/expected_pal_canvas/sp_word_spacing/sp_word_spacing_mask.png b/tests/testdata/control_images/expected_pal_canvas/sp_word_spacing/sp_word_spacing_mask.png
new file mode 100644
index 0000000..f7db90a
Binary files /dev/null and b/tests/testdata/control_images/expected_pal_canvas/sp_word_spacing/sp_word_spacing_mask.png differ
diff --git a/tests/testdata/control_images/expected_pal_canvas_line/sp_curved_placement_above/sp_curved_placement_above.png b/tests/testdata/control_images/expected_pal_canvas_line/sp_curved_placement_above/sp_curved_placement_above.png
index 2195736..08aecbf 100644
Binary files a/tests/testdata/control_images/expected_pal_canvas_line/sp_curved_placement_above/sp_curved_placement_above.png and b/tests/testdata/control_images/expected_pal_canvas_line/sp_curved_placement_above/sp_curved_placement_above.png differ
diff --git a/tests/testdata/control_images/expected_pal_canvas_line/sp_curved_placement_above/sp_curved_placement_above_mask.png b/tests/testdata/control_images/expected_pal_canvas_line/sp_curved_placement_above/sp_curved_placement_above_mask.png
index 31c3799..015aa4b 100644
Binary files a/tests/testdata/control_images/expected_pal_canvas_line/sp_curved_placement_above/sp_curved_placement_above_mask.png and b/tests/testdata/control_images/expected_pal_canvas_line/sp_curved_placement_above/sp_curved_placement_above_mask.png differ
diff --git a/tests/testdata/control_images/expected_pal_canvas_line/sp_curved_placement_below/sp_curved_placement_below.png b/tests/testdata/control_images/expected_pal_canvas_line/sp_curved_placement_below/sp_curved_placement_below.png
index d46334b..c036e7ec 100644
Binary files a/tests/testdata/control_images/expected_pal_canvas_line/sp_curved_placement_below/sp_curved_placement_below.png and b/tests/testdata/control_images/expected_pal_canvas_line/sp_curved_placement_below/sp_curved_placement_below.png differ
diff --git a/tests/testdata/control_images/expected_pal_canvas_line/sp_curved_placement_below/sp_curved_placement_below_mask.png b/tests/testdata/control_images/expected_pal_canvas_line/sp_curved_placement_below/sp_curved_placement_below_mask.png
index 730de01..57d1b24 100644
Binary files a/tests/testdata/control_images/expected_pal_canvas_line/sp_curved_placement_below/sp_curved_placement_below_mask.png and b/tests/testdata/control_images/expected_pal_canvas_line/sp_curved_placement_below/sp_curved_placement_below_mask.png differ
diff --git a/tests/testdata/control_images/expected_pal_canvas_line/sp_curved_placement_online/sp_curved_placement_online.png b/tests/testdata/control_images/expected_pal_canvas_line/sp_curved_placement_online/sp_curved_placement_online.png
index 603b97d..a60b0bb 100644
Binary files a/tests/testdata/control_images/expected_pal_canvas_line/sp_curved_placement_online/sp_curved_placement_online.png and b/tests/testdata/control_images/expected_pal_canvas_line/sp_curved_placement_online/sp_curved_placement_online.png differ
diff --git a/tests/testdata/control_images/expected_pal_canvas_line/sp_curved_placement_online/sp_curved_placement_online_mask.png b/tests/testdata/control_images/expected_pal_canvas_line/sp_curved_placement_online/sp_curved_placement_online_mask.png
index 89dbf8b..c41b629 100644
Binary files a/tests/testdata/control_images/expected_pal_canvas_line/sp_curved_placement_online/sp_curved_placement_online_mask.png and b/tests/testdata/control_images/expected_pal_canvas_line/sp_curved_placement_online/sp_curved_placement_online_mask.png differ
diff --git a/tests/testdata/control_images/expected_pal_composer/sp_img_letter_spacing/sp_img_letter_spacing.png b/tests/testdata/control_images/expected_pal_composer/sp_img_letter_spacing/sp_img_letter_spacing.png
new file mode 100644
index 0000000..ae88a86
Binary files /dev/null and b/tests/testdata/control_images/expected_pal_composer/sp_img_letter_spacing/sp_img_letter_spacing.png differ
diff --git a/tests/testdata/control_images/expected_pal_composer/sp_img_letter_spacing/sp_img_letter_spacing_mask.png b/tests/testdata/control_images/expected_pal_composer/sp_img_letter_spacing/sp_img_letter_spacing_mask.png
new file mode 100644
index 0000000..91577fd
Binary files /dev/null and b/tests/testdata/control_images/expected_pal_composer/sp_img_letter_spacing/sp_img_letter_spacing_mask.png differ
diff --git a/tests/testdata/control_images/expected_pal_composer/sp_img_word_spacing/sp_img_word_spacing.png b/tests/testdata/control_images/expected_pal_composer/sp_img_word_spacing/sp_img_word_spacing.png
new file mode 100644
index 0000000..bad11f5
Binary files /dev/null and b/tests/testdata/control_images/expected_pal_composer/sp_img_word_spacing/sp_img_word_spacing.png differ
diff --git a/tests/testdata/control_images/expected_pal_composer/sp_img_word_spacing/sp_img_word_spacing_mask.png b/tests/testdata/control_images/expected_pal_composer/sp_img_word_spacing/sp_img_word_spacing_mask.png
new file mode 100644
index 0000000..f60d864
Binary files /dev/null and b/tests/testdata/control_images/expected_pal_composer/sp_img_word_spacing/sp_img_word_spacing_mask.png differ
diff --git a/tests/testdata/control_images/expected_pal_composer/sp_pdf_letter_spacing/sp_pdf_letter_spacing.png b/tests/testdata/control_images/expected_pal_composer/sp_pdf_letter_spacing/sp_pdf_letter_spacing.png
new file mode 100644
index 0000000..dbb4c60
Binary files /dev/null and b/tests/testdata/control_images/expected_pal_composer/sp_pdf_letter_spacing/sp_pdf_letter_spacing.png differ
diff --git a/tests/testdata/control_images/expected_pal_composer/sp_pdf_letter_spacing/sp_pdf_letter_spacing_mask.png b/tests/testdata/control_images/expected_pal_composer/sp_pdf_letter_spacing/sp_pdf_letter_spacing_mask.png
new file mode 100644
index 0000000..71ba79b
Binary files /dev/null and b/tests/testdata/control_images/expected_pal_composer/sp_pdf_letter_spacing/sp_pdf_letter_spacing_mask.png differ
diff --git a/tests/testdata/control_images/expected_pal_composer/sp_pdf_word_spacing/sp_pdf_word_spacing.png b/tests/testdata/control_images/expected_pal_composer/sp_pdf_word_spacing/sp_pdf_word_spacing.png
new file mode 100644
index 0000000..6506a8e
Binary files /dev/null and b/tests/testdata/control_images/expected_pal_composer/sp_pdf_word_spacing/sp_pdf_word_spacing.png differ
diff --git a/tests/testdata/control_images/expected_pal_composer/sp_pdf_word_spacing/sp_pdf_word_spacing_mask.png b/tests/testdata/control_images/expected_pal_composer/sp_pdf_word_spacing/sp_pdf_word_spacing_mask.png
new file mode 100644
index 0000000..2f22903
Binary files /dev/null and b/tests/testdata/control_images/expected_pal_composer/sp_pdf_word_spacing/sp_pdf_word_spacing_mask.png differ
diff --git a/tests/testdata/control_images/expected_pal_composer/sp_svg_letter_spacing/sp_svg_letter_spacing.png b/tests/testdata/control_images/expected_pal_composer/sp_svg_letter_spacing/sp_svg_letter_spacing.png
new file mode 100644
index 0000000..13ae419
Binary files /dev/null and b/tests/testdata/control_images/expected_pal_composer/sp_svg_letter_spacing/sp_svg_letter_spacing.png differ
diff --git a/tests/testdata/control_images/expected_pal_composer/sp_svg_letter_spacing/sp_svg_letter_spacing_mask.png b/tests/testdata/control_images/expected_pal_composer/sp_svg_letter_spacing/sp_svg_letter_spacing_mask.png
new file mode 100644
index 0000000..7563c4e
Binary files /dev/null and b/tests/testdata/control_images/expected_pal_composer/sp_svg_letter_spacing/sp_svg_letter_spacing_mask.png differ
diff --git a/tests/testdata/control_images/expected_pal_composer/sp_svg_word_spacing/sp_svg_word_spacing.png b/tests/testdata/control_images/expected_pal_composer/sp_svg_word_spacing/sp_svg_word_spacing.png
new file mode 100644
index 0000000..47924d7
Binary files /dev/null and b/tests/testdata/control_images/expected_pal_composer/sp_svg_word_spacing/sp_svg_word_spacing.png differ
diff --git a/tests/testdata/control_images/expected_pal_composer/sp_svg_word_spacing/sp_svg_word_spacing_mask.png b/tests/testdata/control_images/expected_pal_composer/sp_svg_word_spacing/sp_svg_word_spacing_mask.png
new file mode 100644
index 0000000..d5e8dbc
Binary files /dev/null and b/tests/testdata/control_images/expected_pal_composer/sp_svg_word_spacing/sp_svg_word_spacing_mask.png differ
diff --git a/tests/testdata/control_images/expected_pal_composer_line/sp_img_curved_placement_above/sp_img_curved_placement_above.png b/tests/testdata/control_images/expected_pal_composer_line/sp_img_curved_placement_above/sp_img_curved_placement_above.png
index 2195736..08aecbf 100644
Binary files a/tests/testdata/control_images/expected_pal_composer_line/sp_img_curved_placement_above/sp_img_curved_placement_above.png and b/tests/testdata/control_images/expected_pal_composer_line/sp_img_curved_placement_above/sp_img_curved_placement_above.png differ
diff --git a/tests/testdata/control_images/expected_pal_composer_line/sp_img_curved_placement_above/sp_img_curved_placement_above_mask.png b/tests/testdata/control_images/expected_pal_composer_line/sp_img_curved_placement_above/sp_img_curved_placement_above_mask.png
index a9de5f9..6d4e6e1 100644
Binary files a/tests/testdata/control_images/expected_pal_composer_line/sp_img_curved_placement_above/sp_img_curved_placement_above_mask.png and b/tests/testdata/control_images/expected_pal_composer_line/sp_img_curved_placement_above/sp_img_curved_placement_above_mask.png differ
diff --git a/tests/testdata/control_images/expected_pal_composer_line/sp_img_curved_placement_below/sp_img_curved_placement_below.png b/tests/testdata/control_images/expected_pal_composer_line/sp_img_curved_placement_below/sp_img_curved_placement_below.png
index d46334b..c036e7ec 100644
Binary files a/tests/testdata/control_images/expected_pal_composer_line/sp_img_curved_placement_below/sp_img_curved_placement_below.png and b/tests/testdata/control_images/expected_pal_composer_line/sp_img_curved_placement_below/sp_img_curved_placement_below.png differ
diff --git a/tests/testdata/control_images/expected_pal_composer_line/sp_img_curved_placement_below/sp_img_curved_placement_below_mask.png b/tests/testdata/control_images/expected_pal_composer_line/sp_img_curved_placement_below/sp_img_curved_placement_below_mask.png
index 4c7696b..4579f23 100644
Binary files a/tests/testdata/control_images/expected_pal_composer_line/sp_img_curved_placement_below/sp_img_curved_placement_below_mask.png and b/tests/testdata/control_images/expected_pal_composer_line/sp_img_curved_placement_below/sp_img_curved_placement_below_mask.png differ
diff --git a/tests/testdata/control_images/expected_pal_composer_line/sp_img_curved_placement_online/sp_img_curved_placement_online.png b/tests/testdata/control_images/expected_pal_composer_line/sp_img_curved_placement_online/sp_img_curved_placement_online.png
index 603b97d..a60b0bb 100644
Binary files a/tests/testdata/control_images/expected_pal_composer_line/sp_img_curved_placement_online/sp_img_curved_placement_online.png and b/tests/testdata/control_images/expected_pal_composer_line/sp_img_curved_placement_online/sp_img_curved_placement_online.png differ
diff --git a/tests/testdata/control_images/expected_pal_composer_line/sp_img_curved_placement_online/sp_img_curved_placement_online_mask.png b/tests/testdata/control_images/expected_pal_composer_line/sp_img_curved_placement_online/sp_img_curved_placement_online_mask.png
index afebd87..8b38ff0 100644
Binary files a/tests/testdata/control_images/expected_pal_composer_line/sp_img_curved_placement_online/sp_img_curved_placement_online_mask.png and b/tests/testdata/control_images/expected_pal_composer_line/sp_img_curved_placement_online/sp_img_curved_placement_online_mask.png differ
diff --git a/tests/testdata/control_images/expected_pal_composer_line/sp_pdf_curved_placement_above/sp_pdf_curved_placement_above.png b/tests/testdata/control_images/expected_pal_composer_line/sp_pdf_curved_placement_above/sp_pdf_curved_placement_above.png
index 41d79d1..fd53058 100644
Binary files a/tests/testdata/control_images/expected_pal_composer_line/sp_pdf_curved_placement_above/sp_pdf_curved_placement_above.png and b/tests/testdata/control_images/expected_pal_composer_line/sp_pdf_curved_placement_above/sp_pdf_curved_placement_above.png differ
diff --git a/tests/testdata/control_images/expected_pal_composer_line/sp_pdf_curved_placement_above/sp_pdf_curved_placement_above_mask.png b/tests/testdata/control_images/expected_pal_composer_line/sp_pdf_curved_placement_above/sp_pdf_curved_placement_above_mask.png
index f940725..d2bd597 100644
Binary files a/tests/testdata/control_images/expected_pal_composer_line/sp_pdf_curved_placement_above/sp_pdf_curved_placement_above_mask.png and b/tests/testdata/control_images/expected_pal_composer_line/sp_pdf_curved_placement_above/sp_pdf_curved_placement_above_mask.png differ
diff --git a/tests/testdata/control_images/expected_pal_composer_line/sp_pdf_curved_placement_below/sp_pdf_curved_placement_below.png b/tests/testdata/control_images/expected_pal_composer_line/sp_pdf_curved_placement_below/sp_pdf_curved_placement_below.png
index 67d8198..383d378 100644
Binary files a/tests/testdata/control_images/expected_pal_composer_line/sp_pdf_curved_placement_below/sp_pdf_curved_placement_below.png and b/tests/testdata/control_images/expected_pal_composer_line/sp_pdf_curved_placement_below/sp_pdf_curved_placement_below.png differ
diff --git a/tests/testdata/control_images/expected_pal_composer_line/sp_pdf_curved_placement_below/sp_pdf_curved_placement_below_mask.png b/tests/testdata/control_images/expected_pal_composer_line/sp_pdf_curved_placement_below/sp_pdf_curved_placement_below_mask.png
index 1edbc10..5a3a681 100644
Binary files a/tests/testdata/control_images/expected_pal_composer_line/sp_pdf_curved_placement_below/sp_pdf_curved_placement_below_mask.png and b/tests/testdata/control_images/expected_pal_composer_line/sp_pdf_curved_placement_below/sp_pdf_curved_placement_below_mask.png differ
diff --git a/tests/testdata/control_images/expected_pal_composer_line/sp_pdf_curved_placement_online/sp_pdf_curved_placement_online.png b/tests/testdata/control_images/expected_pal_composer_line/sp_pdf_curved_placement_online/sp_pdf_curved_placement_online.png
index 97537ec..9c44172 100644
Binary files a/tests/testdata/control_images/expected_pal_composer_line/sp_pdf_curved_placement_online/sp_pdf_curved_placement_online.png and b/tests/testdata/control_images/expected_pal_composer_line/sp_pdf_curved_placement_online/sp_pdf_curved_placement_online.png differ
diff --git a/tests/testdata/control_images/expected_pal_composer_line/sp_pdf_curved_placement_online/sp_pdf_curved_placement_online_mask.png b/tests/testdata/control_images/expected_pal_composer_line/sp_pdf_curved_placement_online/sp_pdf_curved_placement_online_mask.png
index 6ae7594..13363c2 100644
Binary files a/tests/testdata/control_images/expected_pal_composer_line/sp_pdf_curved_placement_online/sp_pdf_curved_placement_online_mask.png and b/tests/testdata/control_images/expected_pal_composer_line/sp_pdf_curved_placement_online/sp_pdf_curved_placement_online_mask.png differ
diff --git a/tests/testdata/control_images/expected_pal_composer_line/sp_svg_curved_placement_above/sp_svg_curved_placement_above.png b/tests/testdata/control_images/expected_pal_composer_line/sp_svg_curved_placement_above/sp_svg_curved_placement_above.png
index a9742f3..0360eb4 100644
Binary files a/tests/testdata/control_images/expected_pal_composer_line/sp_svg_curved_placement_above/sp_svg_curved_placement_above.png and b/tests/testdata/control_images/expected_pal_composer_line/sp_svg_curved_placement_above/sp_svg_curved_placement_above.png differ
diff --git a/tests/testdata/control_images/expected_pal_composer_line/sp_svg_curved_placement_above/sp_svg_curved_placement_above_mask.png b/tests/testdata/control_images/expected_pal_composer_line/sp_svg_curved_placement_above/sp_svg_curved_placement_above_mask.png
index 0dc5613..7daa9d8 100644
Binary files a/tests/testdata/control_images/expected_pal_composer_line/sp_svg_curved_placement_above/sp_svg_curved_placement_above_mask.png and b/tests/testdata/control_images/expected_pal_composer_line/sp_svg_curved_placement_above/sp_svg_curved_placement_above_mask.png differ
diff --git a/tests/testdata/control_images/expected_pal_composer_line/sp_svg_curved_placement_below/sp_svg_curved_placement_below.png b/tests/testdata/control_images/expected_pal_composer_line/sp_svg_curved_placement_below/sp_svg_curved_placement_below.png
index 2a7f371..2d3db48 100644
Binary files a/tests/testdata/control_images/expected_pal_composer_line/sp_svg_curved_placement_below/sp_svg_curved_placement_below.png and b/tests/testdata/control_images/expected_pal_composer_line/sp_svg_curved_placement_below/sp_svg_curved_placement_below.png differ
diff --git a/tests/testdata/control_images/expected_pal_composer_line/sp_svg_curved_placement_below/sp_svg_curved_placement_below_mask.png b/tests/testdata/control_images/expected_pal_composer_line/sp_svg_curved_placement_below/sp_svg_curved_placement_below_mask.png
index 14523ec..6821d03 100644
Binary files a/tests/testdata/control_images/expected_pal_composer_line/sp_svg_curved_placement_below/sp_svg_curved_placement_below_mask.png and b/tests/testdata/control_images/expected_pal_composer_line/sp_svg_curved_placement_below/sp_svg_curved_placement_below_mask.png differ
diff --git a/tests/testdata/control_images/expected_pal_composer_line/sp_svg_curved_placement_online/sp_svg_curved_placement_online.png b/tests/testdata/control_images/expected_pal_composer_line/sp_svg_curved_placement_online/sp_svg_curved_placement_online.png
index 4117405..693293b 100644
Binary files a/tests/testdata/control_images/expected_pal_composer_line/sp_svg_curved_placement_online/sp_svg_curved_placement_online.png and b/tests/testdata/control_images/expected_pal_composer_line/sp_svg_curved_placement_online/sp_svg_curved_placement_online.png differ
diff --git a/tests/testdata/control_images/expected_pal_composer_line/sp_svg_curved_placement_online/sp_svg_curved_placement_online_mask.png b/tests/testdata/control_images/expected_pal_composer_line/sp_svg_curved_placement_online/sp_svg_curved_placement_online_mask.png
index 76c53bd..3511a94 100644
Binary files a/tests/testdata/control_images/expected_pal_composer_line/sp_svg_curved_placement_online/sp_svg_curved_placement_online_mask.png and b/tests/testdata/control_images/expected_pal_composer_line/sp_svg_curved_placement_online/sp_svg_curved_placement_online_mask.png differ
diff --git a/tests/testdata/control_images/expected_pal_server/sp_letter_spacing/sp_letter_spacing.png b/tests/testdata/control_images/expected_pal_server/sp_letter_spacing/sp_letter_spacing.png
new file mode 100644
index 0000000..9350bcd
Binary files /dev/null and b/tests/testdata/control_images/expected_pal_server/sp_letter_spacing/sp_letter_spacing.png differ
diff --git a/tests/testdata/control_images/expected_pal_server/sp_letter_spacing/sp_letter_spacing_mask.png b/tests/testdata/control_images/expected_pal_server/sp_letter_spacing/sp_letter_spacing_mask.png
new file mode 100644
index 0000000..300ec88
Binary files /dev/null and b/tests/testdata/control_images/expected_pal_server/sp_letter_spacing/sp_letter_spacing_mask.png differ
diff --git a/tests/testdata/control_images/expected_pal_server/sp_word_spacing/sp_word_spacing.png b/tests/testdata/control_images/expected_pal_server/sp_word_spacing/sp_word_spacing.png
new file mode 100644
index 0000000..a184716
Binary files /dev/null and b/tests/testdata/control_images/expected_pal_server/sp_word_spacing/sp_word_spacing.png differ
diff --git a/tests/testdata/control_images/expected_pal_server/sp_word_spacing/sp_word_spacing_mask.png b/tests/testdata/control_images/expected_pal_server/sp_word_spacing/sp_word_spacing_mask.png
new file mode 100644
index 0000000..091ec72
Binary files /dev/null and b/tests/testdata/control_images/expected_pal_server/sp_word_spacing/sp_word_spacing_mask.png differ
diff --git a/tests/testdata/control_images/expected_pal_server_line/sp_curved_placement_above/sp_curved_placement_above.png b/tests/testdata/control_images/expected_pal_server_line/sp_curved_placement_above/sp_curved_placement_above.png
index 2195736..b3dd3e2 100644
Binary files a/tests/testdata/control_images/expected_pal_server_line/sp_curved_placement_above/sp_curved_placement_above.png and b/tests/testdata/control_images/expected_pal_server_line/sp_curved_placement_above/sp_curved_placement_above.png differ
diff --git a/tests/testdata/control_images/expected_pal_server_line/sp_curved_placement_above/sp_curved_placement_above_mask.png b/tests/testdata/control_images/expected_pal_server_line/sp_curved_placement_above/sp_curved_placement_above_mask.png
index 31c3799..8382fe3 100644
Binary files a/tests/testdata/control_images/expected_pal_server_line/sp_curved_placement_above/sp_curved_placement_above_mask.png and b/tests/testdata/control_images/expected_pal_server_line/sp_curved_placement_above/sp_curved_placement_above_mask.png differ
diff --git a/tests/testdata/control_images/expected_pal_server_line/sp_curved_placement_below/sp_curved_placement_below.png b/tests/testdata/control_images/expected_pal_server_line/sp_curved_placement_below/sp_curved_placement_below.png
index d46334b..abf8a8d 100644
Binary files a/tests/testdata/control_images/expected_pal_server_line/sp_curved_placement_below/sp_curved_placement_below.png and b/tests/testdata/control_images/expected_pal_server_line/sp_curved_placement_below/sp_curved_placement_below.png differ
diff --git a/tests/testdata/control_images/expected_pal_server_line/sp_curved_placement_below/sp_curved_placement_below_mask.png b/tests/testdata/control_images/expected_pal_server_line/sp_curved_placement_below/sp_curved_placement_below_mask.png
index 730de01..d3eaebd 100644
Binary files a/tests/testdata/control_images/expected_pal_server_line/sp_curved_placement_below/sp_curved_placement_below_mask.png and b/tests/testdata/control_images/expected_pal_server_line/sp_curved_placement_below/sp_curved_placement_below_mask.png differ
diff --git a/tests/testdata/control_images/expected_pal_server_line/sp_curved_placement_online/sp_curved_placement_online.png b/tests/testdata/control_images/expected_pal_server_line/sp_curved_placement_online/sp_curved_placement_online.png
index 603b97d..785ae18 100644
Binary files a/tests/testdata/control_images/expected_pal_server_line/sp_curved_placement_online/sp_curved_placement_online.png and b/tests/testdata/control_images/expected_pal_server_line/sp_curved_placement_online/sp_curved_placement_online.png differ
diff --git a/tests/testdata/control_images/expected_pal_server_line/sp_curved_placement_online/sp_curved_placement_online_mask.png b/tests/testdata/control_images/expected_pal_server_line/sp_curved_placement_online/sp_curved_placement_online_mask.png
index 89dbf8b..726c704 100644
Binary files a/tests/testdata/control_images/expected_pal_server_line/sp_curved_placement_online/sp_curved_placement_online_mask.png and b/tests/testdata/control_images/expected_pal_server_line/sp_curved_placement_online/sp_curved_placement_online_mask.png differ
diff --git a/tests/testdata/provider/GetCapabilities.xml b/tests/testdata/provider/GetCapabilities.xml
new file mode 100644
index 0000000..ed593d8
--- /dev/null
+++ b/tests/testdata/provider/GetCapabilities.xml
@@ -0,0 +1,188 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<WMS_Capabilities version="1.3.0" xmlns="http://www.opengis.net/wms" xmlns:sld="http://www.opengis.net/sld" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ms="http://mapserver.gis.umn.edu/mapserver" xsi:schemaLocation="http://www.opengis.net/wms http://schemas.opengis.net/wms/1.3.0/capabilities_1_3_0.xsd http://www.opengis.net/sld http://schemas.opengis.net/sld/1.1.0/sld_capabilities.xsd http://mapserver.gis.umn.edu/mapserver http://localhost:8380/mapserv?service=WMS&v [...]
+ <!-- MapServer version 7.0.1 OUTPUT=PNG OUTPUT=JPEG OUTPUT=KML SUPPORTS=PROJ SUPPORTS=AGG SUPPORTS=FREETYPE SUPPORTS=CAIRO SUPPORTS=SVG_SYMBOLS SUPPORTS=RSVG SUPPORTS=ICONV SUPPORTS=FRIBIDI SUPPORTS=WMS_SERVER SUPPORTS=WMS_CLIENT SUPPORTS=WFS_SERVER SUPPORTS=WFS_CLIENT SUPPORTS=WCS_SERVER SUPPORTS=SOS_SERVER SUPPORTS=FASTCGI SUPPORTS=THREADS SUPPORTS=GEOS INPUT=JPEG INPUT=POSTGIS INPUT=OGR INPUT=GDAL INPUT=SHAPEFILE -->
+ <Service>
+ <Name>WMS</Name>
+ <Title>Test</Title>
+ <Abstract>Test</Abstract>
+ <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="http://localhost:8380/mapserv?"/>
+ <ContactInformation>
+ </ContactInformation>
+ <MaxWidth>5000</MaxWidth>
+ <MaxHeight>5000</MaxHeight>
+ </Service>
+
+ <Capability>
+ <Request>
+ <GetCapabilities>
+ <Format>text/xml</Format>
+ <DCPType>
+ <HTTP>
+ <Get><OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="http://localhost:8380/mapserv?"/></Get>
+ <Post><OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="http://localhost:8380/mapserv?"/></Post>
+ </HTTP>
+ </DCPType>
+ </GetCapabilities>
+ <GetMap>
+ <Format>image/jpeg</Format>
+ <Format>image/png</Format>
+ <DCPType>
+ <HTTP>
+ <Get><OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="http://localhost:8380/mapserv?"/></Get>
+ <Post><OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="http://localhost:8380/mapserv?"/></Post>
+ </HTTP>
+ </DCPType>
+ </GetMap>
+ <GetFeatureInfo>
+ <Format>text/plain</Format>
+ <Format>application/vnd.ogc.gml</Format>
+ <DCPType>
+ <HTTP>
+ <Get><OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="http://localhost:8380/mapserv?"/></Get>
+ <Post><OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="http://localhost:8380/mapserv?"/></Post>
+ </HTTP>
+ </DCPType>
+ </GetFeatureInfo>
+ <sld:DescribeLayer>
+ <Format>text/xml</Format>
+ <DCPType>
+ <HTTP>
+ <Get><OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="http://localhost:8380/mapserv?"/></Get>
+ <Post><OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="http://localhost:8380/mapserv?"/></Post>
+ </HTTP>
+ </DCPType>
+ </sld:DescribeLayer>
+ <sld:GetLegendGraphic>
+ <Format>image/jpeg</Format>
+ <Format>image/png</Format>
+ <Format>image/png; mode=8bit</Format>
+ <DCPType>
+ <HTTP>
+ <Get><OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="http://localhost:8380/mapserv?"/></Get>
+ <Post><OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="http://localhost:8380/mapserv?"/></Post>
+ </HTTP>
+ </DCPType>
+ </sld:GetLegendGraphic>
+ <ms:GetStyles>
+ <Format>text/xml</Format>
+ <DCPType>
+ <HTTP>
+ <Get><OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="http://localhost:8380/mapserv?"/></Get>
+ <Post><OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="http://localhost:8380/mapserv?"/></Post>
+ </HTTP>
+ </DCPType>
+ </ms:GetStyles>
+ </Request>
+ <Exception>
+ <Format>XML</Format>
+ <Format>BLANK</Format>
+ </Exception>
+ <sld:UserDefinedSymbolization SupportSLD="1" UserLayer="0" UserStyle="1" RemoteWFS="0" InlineFeature="0" RemoteWCS="0"/>
+ <Layer>
+ <Name>test</Name>
+ <Title>Test</Title>
+ <Abstract>Test</Abstract>
+ <CRS>EPSG:2056</CRS>
+ <EX_GeographicBoundingBox>
+ <westBoundLongitude>5.01393</westBoundLongitude>
+ <eastBoundLongitude>11.4774</eastBoundLongitude>
+ <southBoundLatitude>45.356</southBoundLatitude>
+ <northBoundLatitude>48.3001</northBoundLatitude>
+ </EX_GeographicBoundingBox>
+ <BoundingBox CRS="EPSG:2056" minx="2.42e+06" miny="1.03e+06" maxx="2.9e+06" maxy="1.35e+06"/>
+ <Layer queryable="1" opaque="0" cascaded="0">
+ <Name>agri_zones</Name>
+ <Title>agri_zones</Title>
+ <CRS>EPSG:2056</CRS>
+ <EX_GeographicBoundingBox>
+ <westBoundLongitude>5.01393</westBoundLongitude>
+ <eastBoundLongitude>11.4774</eastBoundLongitude>
+ <southBoundLatitude>45.356</southBoundLatitude>
+ <northBoundLatitude>48.3001</northBoundLatitude>
+ </EX_GeographicBoundingBox>
+ <BoundingBox CRS="EPSG:2056" minx="2.42e+06" miny="1.03e+06" maxx="2.9e+06" maxy="1.35e+06"/>
+ <MetadataURL type="TC211">
+ <Format>text/html</Format>
+ <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="http://www.example.com/bar"/>
+ </MetadataURL>
+ <Style>
+ <Name>yt_style</Name>
+ <Title>yt_style</Title>
+ <LegendURL width="23" height="19">
+ <Format>image/png</Format>
+ <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="http://www.example.com/yt.png"/>
+ </LegendURL>
+ </Style>
+ <Style>
+ <Name>fb_style</Name>
+ <Title>fb_style</Title>
+ <LegendURL width="23" height="19">
+ <Format>image/png</Format>
+ <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="http://www.example.com/fb.png"/>
+ </LegendURL>
+ </Style>
+ </Layer>
+ <Layer>
+ <Name>cadastre</Name>
+ <Title>cadastre</Title>
+ <Abstract>cadastre</Abstract>
+ <Style>
+ <Name>default</Name>
+ <Title>default</Title>
+ <LegendURL width="88" height="50">
+ <Format>image/png</Format>
+ <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="http://localhost:8380/mapserv?version=1.3.0&service=WMS&request=GetLegendGraphic&sld_version=1.1.0&layer=cadastre&format=image/png&STYLE=default"/>
+ </LegendURL>
+ </Style>
+ <Layer queryable="1" opaque="0" cascaded="0">
+ <Name>buildings</Name>
+ <Title>buildings</Title>
+ <CRS>EPSG:2056</CRS>
+ <EX_GeographicBoundingBox>
+ <westBoundLongitude>5.01393</westBoundLongitude>
+ <eastBoundLongitude>11.4774</eastBoundLongitude>
+ <southBoundLatitude>45.356</southBoundLatitude>
+ <northBoundLatitude>48.3001</northBoundLatitude>
+ </EX_GeographicBoundingBox>
+ <BoundingBox CRS="EPSG:2056" minx="2.42e+06" miny="1.03e+06" maxx="2.9e+06" maxy="1.35e+06"/>
+ <MetadataURL type="TC211">
+ <Format>text/html</Format>
+ <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="http://www.example.com/bar"/>
+ </MetadataURL>
+ <Style>
+ <Name>default</Name>
+ <Title>default</Title>
+ <LegendURL width="88" height="20">
+ <Format>image/png</Format>
+ <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="http://www.example.com/buildings.png"/>
+ </LegendURL>
+ </Style>
+ </Layer>
+ <Layer queryable="1" opaque="0" cascaded="0">
+ <Name>land_surveing_parcels</Name>
+ <Title>land_surveing_parcels</Title>
+ <CRS>EPSG:2056</CRS>
+ <EX_GeographicBoundingBox>
+ <westBoundLongitude>5.01393</westBoundLongitude>
+ <eastBoundLongitude>11.4774</eastBoundLongitude>
+ <southBoundLatitude>45.356</southBoundLatitude>
+ <northBoundLatitude>48.3001</northBoundLatitude>
+ </EX_GeographicBoundingBox>
+ <BoundingBox CRS="EPSG:2056" minx="2.42e+06" miny="1.03e+06" maxx="2.9e+06" maxy="1.35e+06"/>
+ <MetadataURL type="TC211">
+ <Format>text/html</Format>
+ <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="http://www.example.com/bar"/>
+ </MetadataURL>
+ <Style>
+ <Name>default</Name>
+ <Title>default</Title>
+ <LegendURL width="84" height="20">
+ <Format>image/png</Format>
+ <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="http://localhost:8380/mapserv?version=1.3.0&service=WMS&request=GetLegendGraphic&sld_version=1.1.0&layer=land_surveing_parcels&format=image/png&STYLE=default"/>
+ </LegendURL>
+ </Style>
+ </Layer>
+ </Layer>
+ </Layer>
+ </Capability>
+</WMS_Capabilities>
\ No newline at end of file
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-grass/qgis.git
More information about the Pkg-grass-devel
mailing list