21
21
22
22
#include " qgisapp.h"
23
23
#include " qgslogger.h"
24
- #include " qgsmapcanvas.h"
25
24
#include " qgsmaplayer.h"
25
+ #include " qgsrasterlayer.h"
26
26
#include " qgsmaptopixel.h"
27
27
#include " qgspoint.h"
28
28
#include " qgsproject.h"
29
29
#include " qgssymbollayerv2utils.h" // for pointOnLineWithDistance
30
30
#include " qgssymbolv2.h" // for symbology
31
31
#include " qgsrendercontext.h"
32
+ #include " qgsmapcanvas.h"
33
+ #include " qgsmaprenderer.h"
32
34
33
35
#include < QPainter>
34
36
#include < QAction>
42
44
#include < QFile>
43
45
#include < QLocale>
44
46
#include < QDomDocument>
47
+ #include < QMessageBox>
45
48
46
49
// non qt includes
47
50
#include < cmath>
@@ -58,9 +61,13 @@ QgsDecorationGrid::QgsDecorationGrid( QObject* parent )
58
61
: QgsDecorationItem( parent )
59
62
{
60
63
setName ( " Grid" );
64
+
61
65
mLineSymbol = 0 ;
62
66
mMarkerSymbol = 0 ;
63
67
projectRead ();
68
+
69
+ connect ( QgisApp::instance ()->mapCanvas ()->mapRenderer (), SIGNAL ( mapUnitsChanged () ),
70
+ this , SLOT ( checkMapUnitsChanged () ) );
64
71
}
65
72
66
73
QgsDecorationGrid::~QgsDecorationGrid ()
@@ -73,19 +80,25 @@ QgsDecorationGrid::~QgsDecorationGrid()
73
80
74
81
void QgsDecorationGrid::setLineSymbol ( QgsLineSymbolV2* symbol )
75
82
{
76
- delete mLineSymbol ;
83
+ if ( mLineSymbol )
84
+ delete mLineSymbol ;
77
85
mLineSymbol = symbol;
78
86
}
79
87
80
88
void QgsDecorationGrid::setMarkerSymbol ( QgsMarkerSymbolV2* symbol )
81
89
{
82
- delete mMarkerSymbol ;
90
+ if ( mMarkerSymbol )
91
+ delete mMarkerSymbol ;
83
92
mMarkerSymbol = symbol;
84
93
}
85
94
86
95
void QgsDecorationGrid::projectRead ()
87
96
{
88
97
QgsDecorationItem::projectRead ();
98
+
99
+ mEnabled = QgsProject::instance ()->readBoolEntry ( mNameConfig , " /Enabled" , false );
100
+ mMapUnits = ( QGis::UnitType ) QgsProject::instance ()->readNumEntry ( mNameConfig , " /MapUnits" ,
101
+ QGis::UnknownUnit );
89
102
mGridStyle = ( GridStyle ) QgsProject::instance ()->readNumEntry ( mNameConfig , " /Style" ,
90
103
QgsDecorationGrid::Solid );
91
104
mGridIntervalX = QgsProject::instance ()->readDoubleEntry ( mNameConfig , " /IntervalX" , 10 );
@@ -94,8 +107,10 @@ void QgsDecorationGrid::projectRead()
94
107
mGridOffsetY = QgsProject::instance ()->readDoubleEntry ( mNameConfig , " /OffsetY" , 0 );
95
108
mCrossLength = QgsProject::instance ()->readDoubleEntry ( mNameConfig , " /CrossLength" , 3 );
96
109
mShowGridAnnotation = QgsProject::instance ()->readBoolEntry ( mNameConfig , " /ShowAnnotation" , false );
97
- mGridAnnotationPosition = ( GridAnnotationPosition ) QgsProject::instance ()->readNumEntry ( mNameConfig , " /AnnotationPosition" , 0 );
98
- mGridAnnotationDirection = ( GridAnnotationDirection ) QgsProject::instance ()->readNumEntry ( mNameConfig , " /AnnotationDirection" , 0 );
110
+ mGridAnnotationPosition = ( GridAnnotationPosition ) QgsProject::instance ()->readNumEntry ( mNameConfig ,
111
+ " /AnnotationPosition" , 0 );
112
+ mGridAnnotationDirection = ( GridAnnotationDirection ) QgsProject::instance ()->readNumEntry ( mNameConfig ,
113
+ " /AnnotationDirection" , 0 );
99
114
mGridAnnotationFont .fromString ( QgsProject::instance ()->readEntry ( mNameConfig , " /AnnotationFont" , " " ) );
100
115
mAnnotationFrameDistance = QgsProject::instance ()->readDoubleEntry ( mNameConfig , " /AnnotationFrameDistance" , 0 );
101
116
mGridAnnotationPrecision = QgsProject::instance ()->readNumEntry ( mNameConfig , " /AnnotationPrecision" , 3 );
@@ -105,25 +120,25 @@ void QgsDecorationGrid::projectRead()
105
120
QDomElement elem;
106
121
QString xml;
107
122
123
+ if ( mLineSymbol )
124
+ setLineSymbol ( 0 );
108
125
xml = QgsProject::instance ()->readEntry ( mNameConfig , " /LineSymbol" );
109
126
if ( xml != " " )
110
127
{
111
128
doc.setContent ( xml );
112
129
elem = doc.documentElement ();
113
- if ( mLineSymbol )
114
- delete mLineSymbol ;
115
130
mLineSymbol = dynamic_cast <QgsLineSymbolV2*>( QgsSymbolLayerV2Utils::loadSymbol ( elem ) );
116
131
}
117
132
if ( ! mLineSymbol )
118
133
mLineSymbol = new QgsLineSymbolV2 ();
119
134
135
+ if ( mMarkerSymbol )
136
+ setMarkerSymbol ( 0 );
120
137
xml = QgsProject::instance ()->readEntry ( mNameConfig , " /MarkerSymbol" );
121
138
if ( xml != " " )
122
139
{
123
140
doc.setContent ( xml );
124
141
elem = doc.documentElement ();
125
- if ( mMarkerSymbol )
126
- delete mMarkerSymbol ;
127
142
mMarkerSymbol = dynamic_cast <QgsMarkerSymbolV2*>( QgsSymbolLayerV2Utils::loadSymbol ( elem ) );
128
143
}
129
144
if ( ! mMarkerSymbol )
@@ -133,6 +148,8 @@ void QgsDecorationGrid::projectRead()
133
148
void QgsDecorationGrid::saveToProject ()
134
149
{
135
150
QgsDecorationItem::saveToProject ();
151
+ QgsProject::instance ()->writeEntry ( mNameConfig , " /Enabled" , mEnabled );
152
+ QgsProject::instance ()->writeEntry ( mNameConfig , " /MapUnits" , ( int ) mMapUnits );
136
153
QgsProject::instance ()->writeEntry ( mNameConfig , " /Style" , ( int ) mGridStyle );
137
154
QgsProject::instance ()->writeEntry ( mNameConfig , " /IntervalX" , mGridIntervalX );
138
155
QgsProject::instance ()->writeEntry ( mNameConfig , " /IntervalY" , mGridIntervalY );
@@ -709,3 +726,159 @@ QFont QgsDecorationGrid::scaledFontPixelSize( const QFont& font ) const
709
726
return scaledFont;
710
727
}
711
728
729
+ void QgsDecorationGrid::checkMapUnitsChanged ()
730
+ {
731
+ // disable grid if units changed and grid is enabled, and update the canvas
732
+ // this is to avoid problems when CRS changes to/from geographic and projected
733
+ // a better solution would be to change the grid interval, but this is a little tricky
734
+ // note: we could be less picky (e.g. from degrees to DMS)
735
+ QGis::UnitType mapUnits = QgisApp::instance ()->mapCanvas ()->mapRenderer ()->mapUnits ();
736
+ if ( mEnabled && ( mMapUnits != mapUnits ) )
737
+ {
738
+ mEnabled = false ;
739
+ mMapUnits = QGis::UnknownUnit; // make sure isDirty() returns true
740
+ if ( ! QgisApp::instance ()->mapCanvas ()->isFrozen () )
741
+ {
742
+ update ();
743
+ }
744
+ }
745
+ }
746
+
747
+ bool QgsDecorationGrid::isDirty ()
748
+ {
749
+ // checks if stored map units is undefined or different from canvas map units
750
+ // or if interval is 0
751
+ if ( mMapUnits == QGis::UnknownUnit ||
752
+ mMapUnits != QgisApp::instance ()->mapCanvas ()->mapRenderer ()->mapUnits () ||
753
+ mGridIntervalX == 0 || mGridIntervalY == 0 )
754
+ return true ;
755
+ return false ;
756
+ }
757
+
758
+ void QgsDecorationGrid::setDirty ( bool dirty )
759
+ {
760
+ if ( dirty )
761
+ {
762
+ mMapUnits = QGis::UnknownUnit;
763
+ }
764
+ else
765
+ {
766
+ mMapUnits = QgisApp::instance ()->mapCanvas ()->mapRenderer ()->mapUnits ();
767
+ }
768
+ }
769
+
770
+ bool QgsDecorationGrid::getIntervalFromExtent ( double * values, bool useXAxis )
771
+ {
772
+ // get default interval from current extents
773
+ // calculate a default interval that is approx (extent width)/5 , adjusted so that it is a rounded number
774
+ // e.g. 12.7 -> 10 66556 -> 70000
775
+ double interval = 0 ;
776
+ QgsRectangle extent = QgisApp::instance ()->mapCanvas ()->extent ();
777
+ if ( useXAxis )
778
+ interval = ( extent.xMaximum () - extent.xMinimum () ) / 5 ;
779
+ else
780
+ interval = ( extent.yMaximum () - extent.yMinimum () ) / 5 ;
781
+ QgsDebugMsg ( QString ( " interval: %1" ).arg ( interval ) );
782
+ if ( interval != 0 )
783
+ {
784
+ double interval2 = 0 ;
785
+ int factor = pow ( 10 , floor ( log10 ( interval ) ) );
786
+ if ( factor != 0 )
787
+ {
788
+ interval2 = round ( interval / factor ) * factor;
789
+ QgsDebugMsg ( QString ( " interval2: %1" ).arg ( interval2 ) );
790
+ if ( interval2 != 0 )
791
+ interval = interval2;
792
+ }
793
+ }
794
+ values[0 ] = values[1 ] = interval;
795
+ values[2 ] = values[3 ] = 0 ;
796
+ return true ;
797
+ }
798
+
799
+ bool QgsDecorationGrid::getIntervalFromCurrentLayer ( double * values )
800
+ {
801
+ // get current layer and make sure it is a raster layer and CRSs match
802
+ QgsMapLayer* layer = QgisApp::instance ()->mapCanvas ()->currentLayer ();
803
+ if ( ! layer )
804
+ {
805
+ QMessageBox::warning ( 0 , tr ( " Error" ), tr ( " No active layer" ) );
806
+ return false ;
807
+ }
808
+ if ( layer->type () != QgsMapLayer::RasterLayer )
809
+ {
810
+ QMessageBox::warning ( 0 , tr ( " Error" ), tr ( " Please select a raster layer" ) );
811
+ return false ;
812
+ }
813
+ QgsRasterLayer* rlayer = dynamic_cast <QgsRasterLayer*>( layer );
814
+ if ( !rlayer )
815
+ {
816
+ QMessageBox::warning ( 0 , tr ( " Error" ), tr ( " Invalid raster layer" ) );
817
+ return false ;
818
+ }
819
+ const QgsCoordinateReferenceSystem& layerCRS = layer->crs ();
820
+ const QgsCoordinateReferenceSystem& mapCRS =
821
+ QgisApp::instance ()->mapCanvas ()->mapRenderer ()->destinationCrs ();
822
+ // is this the best way to compare CRS? should we also make sure map has OTF enabled?
823
+ // TODO calculate transformed values if necessary
824
+ if ( layerCRS != mapCRS )
825
+ {
826
+ QMessageBox::warning ( 0 , tr ( " Error" ), tr ( " Layer CRS must be equal to project CRS" ) );
827
+ return false ;
828
+ }
829
+
830
+ // TODO add a function in QgsRasterLayer to get x/y resolution from provider
831
+ QgsRectangle extent = rlayer->extent ();
832
+ values[0 ] = fabs ( extent.xMaximum () - extent.xMinimum () ) / rlayer->width ();
833
+ values[1 ] = fabs ( extent.yMaximum () - extent.yMinimum () ) / rlayer->height ();
834
+ // TODO calculate offset - this is a little tricky...
835
+ values[2 ] = values[3 ] = 0 ;
836
+
837
+ QgsDebugMsg ( QString ( " xmax: %1 xmin: %2 width: %3 xInterval: %4" ).arg ( extent.xMaximum () ).arg ( extent.xMinimum () ).arg ( rlayer->width () ).arg ( values[0 ] ) );
838
+
839
+ return true ;
840
+ }
841
+
842
+
843
+ // TODO preliminary code to calculate offset
844
+ // double diff = getClosestPixel( extent.xMaximum(), boundBox2.xMinimum(), boundBox.xMinimum(), dx, True )
845
+
846
+ // // function ported from ftools doVectorGrid.py
847
+ // double getClosestPixel( double startVal, double targetVal, double step, bool isMin )
848
+ // {
849
+ // bool foundVal = false;
850
+ // double tmpVal = startVal;
851
+ // bool backOneStep;
852
+ // // find pixels covering the extent - slightly inneficient b/c loop on all elements before xMin
853
+ // if ( targetVal < startVal )
854
+ // {
855
+ // backOneStep = ! isMin;
856
+ // step = - step;
857
+ // // should make sure we don't go into an infinite loop (shouldn't happen)
858
+ // while ( ! foundVal )
859
+ // {
860
+ // if ( tmpVal <= targetVal )
861
+ // {
862
+ // if ( backOneStep )
863
+ // tmpVal -= step;
864
+ // return tmpVal;
865
+ // }
866
+ // tmpVal += step;
867
+ // }
868
+ // }
869
+ // else
870
+ // {
871
+ // backOneStep = isMin;
872
+ // while ( ! foundVal )
873
+ // {
874
+ // if ( tmpVal >= targetVal )
875
+ // {
876
+ // if ( backOneStep )
877
+ // tmpVal -= step;
878
+ // return tmpVal;
879
+ // }
880
+ // tmpVal += step;
881
+ // }
882
+ // }
883
+ // return 0;
884
+ // }
0 commit comments