1 /***************************************************************************
2                           qgsrectangle.cpp  -  description
3                              -------------------
4     begin                : Sat Jun 22 2002
5     copyright            : (C) 2002 by Gary E.Sherman
6     email                : sherman at mrcc.com
7  ***************************************************************************/
8 
9 /***************************************************************************
10  *                                                                         *
11  *   This program is free software; you can redistribute it and/or modify  *
12  *   it under the terms of the GNU General Public License as published by  *
13  *   the Free Software Foundation; either version 2 of the License, or     *
14  *   (at your option) any later version.                                   *
15  *                                                                         *
16  ***************************************************************************/
17 
18 #include "qgsgeometry.h"
19 #include "qgspointxy.h"
20 #include "qgsrectangle.h"
21 #include "qgslogger.h"
22 #include "qgsbox3d.h"
23 #include "qgspolygon.h"
24 #include "qgslinestring.h"
25 
26 #include <QString>
27 #include <QTextStream>
28 #include <QTransform>
29 
30 #include <algorithm>
31 #include <cmath>
32 #include <limits>
33 
fromWkt(const QString & wkt)34 QgsRectangle QgsRectangle::fromWkt( const QString &wkt )
35 {
36   const QgsGeometry geom = QgsGeometry::fromWkt( wkt );
37   if ( geom.isMultipart() )
38     return QgsRectangle();
39 
40   const QgsPolygonXY poly = geom.asPolygon();
41 
42   if ( poly.size() != 1 )
43     return QgsRectangle();
44 
45   const QgsPolylineXY polyline = geom.asPolygon().at( 0 );
46   if ( polyline.size() == 5 && polyline.at( 0 ) == polyline.at( 4 ) && geom.isGeosValid() )
47     return QgsRectangle( polyline.at( 0 ).x(), polyline.at( 0 ).y(), polyline.at( 2 ).x(), polyline.at( 2 ).y() );
48   else
49     return QgsRectangle();
50 }
51 
fromCenterAndSize(const QgsPointXY & center,double width,double height)52 QgsRectangle QgsRectangle::fromCenterAndSize( const QgsPointXY &center, double width, double height )
53 {
54   const double xMin = center.x() - width / 2.0;
55   const double xMax = xMin + width;
56   const double yMin = center.y() - height / 2.0;
57   const double yMax = yMin + height;
58   return QgsRectangle( xMin, yMin, xMax, yMax );
59 }
60 
scaled(double scaleFactor,const QgsPointXY * center) const61 QgsRectangle QgsRectangle::scaled( double scaleFactor, const QgsPointXY *center ) const
62 {
63   QgsRectangle scaledRect = QgsRectangle( *this );
64   scaledRect.scale( scaleFactor, center );
65   return scaledRect;
66 }
67 
operator -(const QgsVector v) const68 QgsRectangle QgsRectangle::operator-( const QgsVector v ) const
69 {
70   const double xmin = mXmin - v.x();
71   const double xmax = mXmax - v.x();
72   const double ymin = mYmin - v.y();
73   const double ymax = mYmax - v.y();
74   return QgsRectangle( xmin, ymin, xmax, ymax );
75 }
76 
operator +(const QgsVector v) const77 QgsRectangle QgsRectangle::operator+( const QgsVector v ) const
78 {
79   const double xmin = mXmin + v.x();
80   const double xmax = mXmax + v.x();
81   const double ymin = mYmin + v.y();
82   const double ymax = mYmax + v.y();
83   return QgsRectangle( xmin, ymin, xmax, ymax );
84 }
85 
operator -=(const QgsVector v)86 QgsRectangle &QgsRectangle::operator-=( const QgsVector v )
87 {
88   mXmin -= v.x();
89   mXmax -= v.x();
90   mYmin -= v.y();
91   mYmax -= v.y();
92   return *this;
93 }
94 
operator +=(const QgsVector v)95 QgsRectangle &QgsRectangle::operator+=( const QgsVector v )
96 {
97   mXmin += v.x();
98   mXmax += v.x();
99   mYmin += v.y();
100   mYmax += v.y();
101   return *this;
102 }
103 
asWktCoordinates() const104 QString QgsRectangle::asWktCoordinates() const
105 {
106   QString rep =
107     qgsDoubleToString( mXmin ) + ' ' + qgsDoubleToString( mYmin ) + QLatin1String( ", " ) +
108     qgsDoubleToString( mXmax ) + ' ' + qgsDoubleToString( mYmax );
109 
110   return rep;
111 }
112 
asWktPolygon() const113 QString QgsRectangle::asWktPolygon() const
114 {
115   QString rep =
116     QLatin1String( "POLYGON((" ) +
117     qgsDoubleToString( mXmin ) + ' ' + qgsDoubleToString( mYmin ) + QLatin1String( ", " ) +
118     qgsDoubleToString( mXmax ) + ' ' + qgsDoubleToString( mYmin ) + QLatin1String( ", " ) +
119     qgsDoubleToString( mXmax ) + ' ' + qgsDoubleToString( mYmax ) + QLatin1String( ", " ) +
120     qgsDoubleToString( mXmin ) + ' ' + qgsDoubleToString( mYmax ) + QLatin1String( ", " ) +
121     qgsDoubleToString( mXmin ) + ' ' + qgsDoubleToString( mYmin ) +
122     QStringLiteral( "))" );
123 
124   return rep;
125 }
126 
toString(int precision) const127 QString QgsRectangle::toString( int precision ) const
128 {
129   QString rep;
130 
131   if ( precision < 0 )
132   {
133     precision = 0;
134     if ( ( width() < 10 || height() < 10 ) && ( width() > 0 && height() > 0 ) )
135     {
136       precision = static_cast<int>( std::ceil( -1.0 * std::log10( std::min( width(), height() ) ) ) ) + 1;
137       // sanity check
138       if ( precision > 20 )
139         precision = 20;
140     }
141   }
142 
143   if ( isEmpty() )
144     rep = QStringLiteral( "Empty" );
145   else
146     rep = QStringLiteral( "%1,%2 : %3,%4" )
147           .arg( mXmin, 0, 'f', precision )
148           .arg( mYmin, 0, 'f', precision )
149           .arg( mXmax, 0, 'f', precision )
150           .arg( mYmax, 0, 'f', precision );
151 
152   QgsDebugMsgLevel( QStringLiteral( "Extents : %1" ).arg( rep ), 4 );
153 
154   return rep;
155 }
156 
asPolygon() const157 QString QgsRectangle::asPolygon() const
158 {
159 //   QString rep = tmp.sprintf("%16f %16f,%16f %16f,%16f %16f,%16f %16f,%16f %16f",
160 //     xmin, ymin, xmin, ymax, xmax, ymax, xmax, ymin, xmin, ymin);
161   QString rep;
162 
163   QTextStream foo( &rep );
164 
165   foo.setRealNumberPrecision( 8 );
166   foo.setRealNumberNotation( QTextStream::FixedNotation );
167   // NOTE: a polygon isn't a polygon unless its closed. In the case of
168   //       a rectangle, that means 5 points (last == first)
169   foo
170       << mXmin << ' ' << mYmin << ", "
171       << mXmin << ' ' << mYmax << ", "
172       << mXmax << ' ' << mYmax << ", "
173       << mXmax << ' ' << mYmin << ", "
174       << mXmin << ' ' << mYmin;
175 
176   return rep;
177 
178 }
179 
toBox3d(double zMin,double zMax) const180 QgsBox3d QgsRectangle::toBox3d( double zMin, double zMax ) const
181 {
182   return QgsBox3d( mXmin, mYmin, zMin, mXmax, mYmax, zMax );
183 }
184 
snappedToGrid(double spacing) const185 QgsRectangle QgsRectangle::snappedToGrid( double spacing ) const
186 {
187   // helper function
188   auto gridifyValue = []( double value, double spacing ) -> double
189   {
190     if ( spacing > 0 )
191       return  std::round( value / spacing ) * spacing;
192     else
193       return value;
194   };
195 
196   return QgsRectangle(
197            gridifyValue( mXmin, spacing ),
198            gridifyValue( mYmin, spacing ),
199            gridifyValue( mXmax, spacing ),
200            gridifyValue( mYmax, spacing )
201          );
202 }
203 
operator <<(QDataStream & out,const QgsRectangle & rectangle)204 QDataStream &operator<<( QDataStream &out, const QgsRectangle &rectangle )
205 {
206   out << rectangle.xMinimum() << rectangle.yMinimum() << rectangle.xMaximum() << rectangle.yMaximum();
207   return out;
208 }
209 
operator >>(QDataStream & in,QgsRectangle & rectangle)210 QDataStream &operator>>( QDataStream &in, QgsRectangle &rectangle )
211 {
212   double xmin, ymin, xmax, ymax;
213   in >> xmin >> ymin >> xmax >> ymax;
214   rectangle.setXMinimum( xmin );
215   rectangle.setYMinimum( ymin );
216   rectangle.setXMaximum( xmax );
217   rectangle.setYMaximum( ymax );
218   return in;
219 }
220