利用GIS加载经纬网的SHP数据可以显示经纬网数据,但是QGIS本身也具备显示经纬网的功能。
QGIS显示经纬网数据利用类:QgsDecorationGrid 。这个类可以构造一个SHP图层来显示出来。
但是我并没有使用QGIS库中的这个类,而是直接使用的源码,把这个类进行了改造,并且可以修改样式(颜色、字体等信息)。
先贴上修改过的类文件:
#ifndef QGSDECORATIONGRID_H
#define QGSDECORATIONGRID_H
#include "qgsdecorationitem.h"
#include <qgis.h>
class QPainter;
class QgsLineSymbolV2;
class QgsMarkerSymbolV2;
#include <QColor>
#include <QPen>
#include <QFont>
class QgsDecorationGrid : public QgsDecorationItem {
Q_OBJECT
public:
//! Constructor
QgsDecorationGrid(QObject* parent = NULL, QgsMapCanvas* Canvas = NULL);
//! Destructor
virtual ~QgsDecorationGrid();
enum GridStyle {
Line = 0, //solid lines
Marker = 1 //user-defined marker
};
enum GridAnnotationPosition {
InsideMapFrame = 0,
OutsideMapFrame
};
enum GridAnnotationDirection {
Horizontal = 0,
Vertical,
HorizontalAndVertical,
BoundaryDirection
};
/**Sets coordinate grid style. */
void setGridStyle(GridStyle style) { mGridStyle = style; }
GridStyle gridStyle() const { return mGridStyle; }
/**Sets coordinate interval in x-direction for composergrid. */
void setGridIntervalX(double interval) { mGridIntervalX = interval; }
double gridIntervalX() const { return mGridIntervalX; }
/**Sets coordinate interval in y-direction for composergrid. */
void setGridIntervalY(double interval) { mGridIntervalY = interval; }
double gridIntervalY() const { return mGridIntervalY; }
/**Sets x-coordinate offset for composer grid */
void setGridOffsetX(double offset) { mGridOffsetX = offset; }
double gridOffsetX() const { return mGridOffsetX; }
/**Sets y-coordinate offset for composer grid */
void setGridOffsetY(double offset) { mGridOffsetY = offset; }
double gridOffsetY() const { return mGridOffsetY; }
/**Sets the pen to draw composer grid */
void setGridPen(const QPen& p) { mGridPen = p; }
QPen gridPen() const { return mGridPen; }
/**Sets with of grid pen */
void setGridPenWidth(double w) { mGridPen.setWidthF(w); }
/**Sets the color of the grid pen */
void setGridPenColor(const QColor& c) { mGridPen.setColor(c); }
/**Sets font for grid annotations */
void setGridAnnotationFont(const QFont& f) { mGridAnnotationFont = f; }
QFont gridAnnotationFont() const { return mGridAnnotationFont; }
/**Sets coordinate precision for grid annotations */
void setGridAnnotationPrecision(int p) { mGridAnnotationPrecision = p; }
int gridAnnotationPrecision() const { return mGridAnnotationPrecision; }
/**Sets flag if grid annotation should be shown */
void setShowGridAnnotation(bool show) { mShowGridAnnotation = show; }
bool showGridAnnotation() const { return mShowGridAnnotation; }
/**Sets position of grid annotations. Possibilities are inside or outside of the map frame */
void setGridAnnotationPosition(GridAnnotationPosition p) { mGridAnnotationPosition = p; }
GridAnnotationPosition gridAnnotationPosition() const { return mGridAnnotationPosition; }
/**Sets distance between map frame and annotations */
void setAnnotationFrameDistance(double d) { mAnnotationFrameDistance = d; }
double annotationFrameDistance() const { return mAnnotationFrameDistance; }
/**Sets grid annotation direction. Can be horizontal, vertical, direction of axis and horizontal and vertical */
void setGridAnnotationDirection(GridAnnotationDirection d) { mGridAnnotationDirection = d; }
GridAnnotationDirection gridAnnotationDirection() const { return mGridAnnotationDirection; }
/**Sets length of the cros segments (if grid style is cross) */
/* void setCrossLength( double l ) {mCrossLength = l;} */
/* double crossLength() {return mCrossLength;} */
/**Set symbol that is used to draw grid lines. Takes ownership*/
void setLineSymbol(QgsLineSymbolV2* symbol);
const QgsLineSymbolV2* lineSymbol() const { return mLineSymbol; }
/**Set symbol that is used to draw markers. Takes ownership*/
void setMarkerSymbol(QgsMarkerSymbolV2* symbol);
const QgsMarkerSymbolV2* markerSymbol() const { return mMarkerSymbol; }
/**Sets map unit type */
void setMapUnits(QGis::UnitType t) { mMapUnits = t; }
QGis::UnitType mapUnits() { return mMapUnits; }
/**Set mapUnits value */
void setDirty(bool dirty = true);
bool isDirty();
/**Computes interval that is approx. 1/5 of canvas extent */
bool getIntervalFromExtent(double* values, bool useXAxis = true);
/**Computes interval from current raster layer */
bool getIntervalFromCurrentLayer(double* values);
double getDefaultInterval(bool useXAxis = true);
void GridsetEnabled(bool b);
public slots:
//! set values on the gui when a project is read or the gui first loaded
void projectRead() override;
//! save values to the project
void saveToProject() override;
//! this does the meaty bit of the work
void render(QPainter *) override;
//! Show the dialog box
void run() override;
//! check that map units changed and disable if necessary
void checkMapUnitsChanged();
private:
/**Enum for different frame borders*/
enum Border {
Left,
Right,
Bottom,
Top
};
QgsMapCanvas* mapCanvas;
/** Line or Symbol */
GridStyle mGridStyle;
/**Grid line interval in x-direction (map units)*/
double mGridIntervalX;
/**Grid line interval in y-direction (map units)*/
double mGridIntervalY;
/**Grid line offset in x-direction*/
double mGridOffsetX;
/**Grid line offset in y-direction*/
double mGridOffsetY;
/**Grid line pen*/
QPen mGridPen;
/**Font for grid line annotation*/
QFont mGridAnnotationFont;
/**Digits after the dot*/
int mGridAnnotationPrecision;
/**True if coordinate values should be drawn*/
bool mShowGridAnnotation;
/**Annotation position inside or outside of map frame*/
GridAnnotationPosition mGridAnnotationPosition;
/**Distance between map frame and annotation*/
double mAnnotationFrameDistance;
/**Annotation can be horizontal / vertical or different for axes*/
GridAnnotationDirection mGridAnnotationDirection;
/**The length of the cross sides for mGridStyle Cross*/
/* double mCrossLength; */
QgsLineSymbolV2* mLineSymbol;
QgsMarkerSymbolV2* mMarkerSymbol;
QGis::UnitType mMapUnits;
/**Draw coordinates for mGridAnnotationType Coordinate
@param p drawing painter
@param hLines horizontal coordinate lines in item coordinates
@param vLines vertical coordinate lines in item coordinates*/
void drawCoordinateAnnotations(QPainter* p, const QList< QPair< qreal, QLineF > >& hLines, const QList< QPair< qreal, QLineF > >& vLines);
void drawCoordinateAnnotation(QPainter* p, const QPointF& pos, QString annotationString);
/**Draws a single annotation
@param p drawing painter
@param pos item coordinates where to draw
@param rotation text rotation
@param annotationText the text to draw*/
void drawAnnotation(QPainter* p, const QPointF& pos, int rotation, const QString& annotationText);
/**Returns the grid lines with associated coordinate value
@return 0 in case of success*/
int xGridLines(QList< QPair< qreal, QLineF > >& lines) const;
/**Returns the grid lines for the y-coordinates. Not vertical in case of rotation
@return 0 in case of success*/
int yGridLines(QList< QPair< qreal, QLineF > >& lines) const;
/**Returns the item border of a point (in item coordinates)*/
Border borderForLineCoord(const QPointF& point, QPainter* p) const;
/**Draws Text. Takes care about all the composer specific issues (calculation to pixel, scaling of font and painter
to work around the Qt font bug)*/
void drawText(QPainter* p, double x, double y, const QString& text, const QFont& font) const;
/**Like the above, but with a rectangle for multiline text*/
void drawText(QPainter* p, const QRectF& rect, const QString& text, const QFont& font, Qt::AlignmentFlag halignment = Qt::AlignLeft, Qt::AlignmentFlag valignment = Qt::AlignTop) const;
/**Returns the font width in millimeters (considers upscaling and downscaling with FONT_WORKAROUND_SCALE*/
double textWidthMillimeters(const QFont& font, const QString& text) const;
/**Returns the font height of a character in millimeters. */
double fontHeightCharacterMM(const QFont& font, const QChar& c) const;
/**Returns the font ascent in Millimeters (considers upscaling and downscaling with FONT_WORKAROUND_SCALE*/
double fontAscentMillimeters(const QFont& font) const;
/**Calculates font to from point size to pixel size*/
double pixelFontSize(double pointSize) const;
/**Returns a font where size is in pixel and font size is upscaled with FONT_WORKAROUND_SCALE*/
QFont scaledFontPixelSize(const QFont& font) const;
/* friend class QgsDecorationGridDialog; */
};
#endif
#include "qgsdecorationgrid.h"
#include "qgslogger.h"
#include "qgsmaplayer.h"
#include "qgsrasterlayer.h"
#include "qgsmaptopixel.h"
#include "qgspoint.h"
#include "qgsproject.h"
#include "qgssymbollayerv2utils.h" //for pointOnLineWithDistance
#include "qgssymbolv2.h" //for symbology
#include "qgsmarkersymbollayerv2.h"
#include "qgsrendercontext.h"
#include "qgsmapcanvas.h"
#include "qgsmaprenderer.h"
#include <QPainter>
#include <QAction>
#include <QPen>
#include <QPolygon>
#include <QString>
#include <QFontMetrics>
#include <QFont>
#include <QColor>
#include <QMenu>
#include <QFile>
#include <QLocale>
#include <QDomDocument>
#include <QMessageBox>
//non qt includes
#include <cmath>
#define FONT_WORKAROUND_SCALE 10 //scale factor for upscaling fontsize and downscaling painter
QgsDecorationGrid::QgsDecorationGrid(QObject* parent, QgsMapCanvas* Canvas)
: QgsDecorationItem(parent) {
setName("Grid");
mapCanvas = Canvas;
mLineSymbol = 0;
mMarkerSymbol = 0;
projectRead();
connect(mapCanvas, SIGNAL(mapUnitsChanged()),
this, SLOT(checkMapUnitsChanged()));
}
void QgsDecorationGrid::GridsetEnabled(bool b) {
setEnabled(b);
mapCanvas->refresh();
}
QgsDecorationGrid::~QgsDecorationGrid() {
if (mLineSymbol) {
delete mLineSymbol;
mLineSymbol = NULL;
}
if (mMarkerSymbol) {
delete mMarkerSymbol;
mMarkerSymbol = NULL;
}
}
void QgsDecorationGrid::setLineSymbol(QgsLineSymbolV2* symbol) {
if (mLineSymbol)
delete mLineSymbol;
mLineSymbol = symbol;
}
void QgsDecorationGrid::setMarkerSymbol(QgsMarkerSymbolV2* symbol) {
if (mMarkerSymbol)
delete mMarkerSymbol;
mMarkerSymbol = symbol;
}
void QgsDecorationGrid::projectRead() {
QgsDecorationItem::projectRead();
mEnabled = QgsProject::instance()->readBoolEntry(mNameConfig, "/Enabled", false);
mMapUnits = (QGis::UnitType) QgsProject::instance()->readNumEntry(mNameConfig, "/MapUnits",
QGis::UnknownUnit);
mGridStyle = (GridStyle)QgsProject::instance()->readNumEntry(mNameConfig, "/Style",
QgsDecorationGrid::Line);
mGridIntervalX = QgsProject::instance()->readDoubleEntry(mNameConfig, "/IntervalX", 10);
mGridIntervalY = QgsProject::instance()->readDoubleEntry(mNameConfig, "/IntervalY", 10);
mGridOffsetX = QgsProject::instance()->readDoubleEntry(mNameConfig, "/OffsetX", 0);
mGridOffsetY = QgsProject::instance()->readDoubleEntry(mNameConfig, "/OffsetY", 0);
mShowGridAnnotation = QgsProject::instance()->readBoolEntry(mNameConfig, "/ShowAnnotation", false);
mGridAnnotationPosition = InsideMapFrame; // don't allow outside frame, doesn't make sense
mGridAnnotationDirection = (GridAnnotationDirection)QgsProject::instance()->readNumEntry(mNameConfig,
"/AnnotationDirection", 0);
QString fontStr = QgsProject::instance()->readEntry(mNameConfig, "/AnnotationFont", "");
if (fontStr != "") {
mGridAnnotationFont.fromString(fontStr);
} else {
mGridAnnotationFont = QFont();
// TODO fix font scaling problem - put a slightly large font for now
mGridAnnotationFont.setPointSize(16);
}
mAnnotationFrameDistance = QgsProject::instance()->readDoubleEntry(mNameConfig, "/AnnotationFrameDistance", 0);
mGridAnnotationPrecision = QgsProject::instance()->readNumEntry(mNameConfig, "/AnnotationPrecision", 0);
// read symbol info from xml
QDomDocument doc;
QDomElement elem;
QString xml;
if (mLineSymbol)
setLineSymbol(0);
xml = QgsProject::instance()->readEntry(mNameConfig, "/LineSymbol");
if (xml != "") {
doc.setContent(xml);
elem = doc.documentElement();
mLineSymbol = QgsSymbolLayerV2Utils::loadSymbol<QgsLineSymbolV2>(elem);
}
if (!mLineSymbol)
mLineSymbol = new QgsLineSymbolV2();
if (mMarkerSymbol)
setMarkerSymbol(0);
xml = QgsProject::instance()->readEntry(mNameConfig, "/MarkerSymbol");
if (xml != "") {
doc.setContent(xml);
elem = doc.documentElement();
mMarkerSymbol = QgsSymbolLayerV2Utils::loadSymbol<QgsMarkerSymbolV2>(elem);
}
if (!mMarkerSymbol) {
// set default symbol : cross with width=3
QgsSymbolLayerV2List symbolList;
symbolList << new QgsSimpleMarkerSymbolLayerV2("cross", DEFAULT_SIMPLEMARKER_COLOR,
DEFAULT_SIMPLEMARKER_BORDERCOLOR, 3, 0);
mMarkerSymbol = new QgsMarkerSymbolV2(symbolList);
}
}
void QgsDecorationGrid::saveToProject() {
QgsDecorationItem::saveToProject();
QgsProject::instance()->writeEntry(mNameConfig, "/Enabled", mEnabled);
QgsProject::instance()->writeEntry(mNameConfig, "/MapUnits", (int)mMapUnits);
QgsProject::instance()->writeEntry(mNameConfig, "/Style", (int)mGridStyle);
QgsProject::instance()->writeEntry(mNameConfig, "/IntervalX", mGridIntervalX);
QgsProject::instance()->writeEntry(mNameConfig, "/IntervalY", mGridIntervalY);
QgsProject::instance()->writeEntry(mNameConfig, "/OffsetX", mGridOffsetX);
QgsProject::instance()->writeEntry(mNameConfig, "/OffsetY", mGridOffsetY);
QgsProject::instance()->writeEntry(mNameConfig, "/ShowAnnotation", mShowGridAnnotation);
QgsProject::instance()->writeEntry(mNameConfig, "/AnnotationDirection", (int)mGridAnnotationDirection);
QgsProject::instance()->writeEntry(mNameConfig, "/AnnotationFont", mGridAnnotationFont.toString());
QgsProject::instance()->writeEntry(mNameConfig, "/AnnotationFrameDistance", mAnnotationFrameDistance);
QgsProject::instance()->writeEntry(mNameConfig, "/AnnotationPrecision", mGridAnnotationPrecision);
// write symbol info to xml
QDomDocument doc;
QDomElement elem;
if (mLineSymbol) {
elem = QgsSymbolLayerV2Utils::saveSymbol("line symbol", mLineSymbol, doc);
doc.appendChild(elem);
// FIXME this works, but XML will not be valid as < is replaced by <
QgsProject::instance()->writeEntry(mNameConfig, "/LineSymbol", doc.toString());
}
if (mMarkerSymbol) {
doc.setContent(QString(""));
elem = QgsSymbolLayerV2Utils::saveSymbol("marker symbol", mMarkerSymbol, doc);
doc.appendChild(elem);
QgsProject::instance()->writeEntry(mNameConfig, "/MarkerSymbol", doc.toString());
}
}
void QgsDecorationGrid::run() {
update();
mapCanvas->refresh();
}
void QgsDecorationGrid::render(QPainter * p) {
if (!mEnabled)
return;
QList< QPair< qreal, QLineF > > verticalLines;
yGridLines(verticalLines);
QList< QPair< qreal, QLineF > > horizontalLines;
xGridLines(horizontalLines);
QList< QPair< qreal, QLineF > >::const_iterator vIt = verticalLines.constBegin();
QList< QPair< qreal, QLineF > >::const_iterator hIt = horizontalLines.constBegin();
//simpler approach: draw vertical lines first, then horizontal ones
if (mGridStyle == QgsDecorationGrid::Line) {
if (!mLineSymbol)
return;
QgsRenderContext context = QgsRenderContext::fromMapSettings(mapCanvas->mapSettings());
context.setPainter(p);
mLineSymbol->startRender(context, 0);
for (; vIt != verticalLines.constEnd(); ++vIt) {
QVector<QPointF> poly;
poly << vIt->second.p1() << vIt->second.p2();
mLineSymbol->renderPolyline(QPolygonF(poly), 0, context);
}
for (; hIt != horizontalLines.constEnd(); ++hIt) {
QVector<QPointF> poly;
poly << hIt->second.p1() << hIt->second.p2();
mLineSymbol->renderPolyline(QPolygonF(poly), 0, context);
}
mLineSymbol->stopRender(context);
}
else //marker
{
if (!mMarkerSymbol)
return;
QgsRenderContext context = QgsRenderContext::fromMapSettings(mapCanvas->mapSettings());
context.setPainter(p);
mMarkerSymbol->startRender(context, 0);
QPointF intersectionPoint;
for (; vIt != verticalLines.constEnd(); ++vIt) {
//test for intersection with every horizontal line
hIt = horizontalLines.constBegin();
for (; hIt != horizontalLines.constEnd(); ++hIt) {
if (hIt->second.intersect(vIt->second, &intersectionPoint) == QLineF::BoundedIntersection) {
mMarkerSymbol->renderPoint(intersectionPoint, 0, context);
}
}
}
mMarkerSymbol->stopRender(context);
}
if (mShowGridAnnotation) {
drawCoordinateAnnotations(p, horizontalLines, verticalLines);
}
}
void QgsDecorationGrid::drawCoordinateAnnotations(QPainter* p, const QList< QPair< qreal, QLineF > >& hLines, const QList< QPair< qreal, QLineF > >& vLines) {
if (!p) {
return;
}
QString currentAnnotationString;
QList< QPair< qreal, QLineF > >::const_iterator it = hLines.constBegin();
for (; it != hLines.constEnd(); ++it) {
currentAnnotationString = QString::number(it->first, 'f', mGridAnnotationPrecision);
drawCoordinateAnnotation(p, it->second.p1(), currentAnnotationString);
drawCoordinateAnnotation(p, it->second.p2(), currentAnnotationString);
}
it = vLines.constBegin();
for (; it != vLines.constEnd(); ++it) {
currentAnnotationString = QString::number(it->first, 'f', mGridAnnotationPrecision);
drawCoordinateAnnotation(p, it->second.p1(), currentAnnotationString);
drawCoordinateAnnotation(p, it->second.p2(), currentAnnotationString);
}
}
void QgsDecorationGrid::drawCoordinateAnnotation(QPainter* p, const QPointF& pos, QString annotationString) {
Border frameBorder = borderForLineCoord(pos, p);
double textWidth = textWidthMillimeters(mGridAnnotationFont, annotationString);
//relevant for annotations is the height of digits
double textHeight = fontHeightCharacterMM(mGridAnnotationFont, QChar('0'));
double xpos = pos.x();
double ypos = pos.y();
int rotation = 0;
if (frameBorder == Left) {
if (mGridAnnotationPosition == InsideMapFrame) {
if (mGridAnnotationDirection == Vertical || mGridAnnotationDirection == BoundaryDirection) {
xpos += textHeight + mAnnotationFrameDistance;
ypos += textWidth / 2.0;
rotation = 270;
} else {
xpos += mAnnotationFrameDistance;
ypos += textHeight / 2.0;
}
} else //Outside map frame
{
if (mGridAnnotationDirection == Vertical || mGridAnnotationDirection == BoundaryDirection) {
xpos -= mAnnotationFrameDistance;
ypos += textWidth / 2.0;
rotation = 270;
} else {
xpos -= textWidth + mAnnotationFrameDistance;
ypos += textHeight / 2.0;
}
}
} else if (frameBorder == Right) {
if (mGridAnnotationPosition == InsideMapFrame) {
if (mGridAnnotationDirection == Vertical || mGridAnnotationDirection == BoundaryDirection) {
xpos -= mAnnotationFrameDistance;
ypos += textWidth / 2.0;
rotation = 270;
} else //Horizontal
{
xpos -= textWidth + mAnnotationFrameDistance;
ypos += textHeight / 2.0;
}
} else //OutsideMapFrame
{
if (mGridAnnotationDirection == Vertical || mGridAnnotationDirection == BoundaryDirection) {
xpos += textHeight + mAnnotationFrameDistance;
ypos += textWidth / 2.0;
rotation = 270;
} else //Horizontal
{
xpos += mAnnotationFrameDistance;
ypos += textHeight / 2.0;
}
}
} else if (frameBorder == Bottom) {
if (mGridAnnotationPosition == InsideMapFrame) {
if (mGridAnnotationDirection == Horizontal || mGridAnnotationDirection == BoundaryDirection) {
ypos -= mAnnotationFrameDistance;
xpos -= textWidth / 2.0;
} else //Vertical
{
xpos += textHeight / 2.0;
ypos -= mAnnotationFrameDistance;
rotation = 270;
}
} else //OutsideMapFrame
{
if (mGridAnnotationDirection == Horizontal || mGridAnnotationDirection == BoundaryDirection) {
ypos += mAnnotationFrameDistance + textHeight;
xpos -= textWidth / 2.0;
} else //Vertical
{
xpos += textHeight / 2.0;
ypos += textWidth + mAnnotationFrameDistance;
rotation = 270;
}
}
} else //Top
{
if (mGridAnnotationPosition == InsideMapFrame) {
if (mGridAnnotationDirection == Horizontal || mGridAnnotationDirection == BoundaryDirection) {
xpos -= textWidth / 2.0;
ypos += textHeight + mAnnotationFrameDistance;
} else //Vertical
{
xpos += textHeight / 2.0;
ypos += textWidth + mAnnotationFrameDistance;
rotation = 270;
}
} else //OutsideMapFrame
{
if (mGridAnnotationDirection == Horizontal || mGridAnnotationDirection == BoundaryDirection) {
xpos -= textWidth / 2.0;
ypos -= mAnnotationFrameDistance;
} else //Vertical
{
xpos += textHeight / 2.0;
ypos -= mAnnotationFrameDistance;
rotation = 270;
}
}
}
drawAnnotation(p, QPointF(xpos, ypos), rotation, annotationString);
}
void QgsDecorationGrid::drawAnnotation(QPainter* p, const QPointF& pos, int rotation, const QString& annotationText) {
p->save();
p->translate(pos);
p->rotate(rotation);
drawText(p, 0, 0, annotationText, mGridAnnotationFont);
p->restore();
}
QPolygonF canvasPolygon(QgsMapCanvas *canvas) {
QPolygonF poly;
const QgsMapCanvas& mapCanvas = *canvas;
const QgsMapSettings& mapSettings = mapCanvas.mapSettings();
return mapSettings.visiblePolygon();
}
bool clipByRect(QLineF& line, const QPolygonF& rect) {
QVector<QLineF> borderLines;
borderLines << QLineF(rect.at(0), rect.at(1));
borderLines << QLineF(rect.at(1), rect.at(2));
borderLines << QLineF(rect.at(2), rect.at(3));
borderLines << QLineF(rect.at(3), rect.at(0));
QList<QPointF> intersectionList;
QVector<QLineF>::const_iterator it = borderLines.constBegin();
for (; it != borderLines.constEnd(); ++it) {
QPointF intersectionPoint;
if (it->intersect(line, &intersectionPoint) == QLineF::BoundedIntersection) {
intersectionList.push_back(intersectionPoint);
if (intersectionList.size() >= 2) {
break; //we already have two intersections, skip further tests
}
}
}
if (intersectionList.size() < 2) return false; // no intersection
line = QLineF(intersectionList.at(0), intersectionList.at(1));
return true;
}
QPolygonF canvasExtent(QgsMapCanvas *canvas) {
QPolygonF poly;
QgsRectangle extent = (*canvas).extent();
poly << QPointF(extent.xMinimum(), extent.yMaximum());
poly << QPointF(extent.xMaximum(), extent.yMaximum());
poly << QPointF(extent.xMaximum(), extent.yMinimum());
poly << QPointF(extent.xMinimum(), extent.yMinimum());
return poly;
}
int QgsDecorationGrid::xGridLines(QList< QPair< qreal, QLineF > >& lines) const {
// prepare horizontal lines
lines.clear();
if (mGridIntervalY <= 0.0) {
return 1;
}
const QgsMapCanvas& mapCanvass = *mapCanvas;
const QgsMapSettings& mapSettings = mapCanvass.mapSettings();
const QgsMapToPixel& m2p = mapSettings.mapToPixel();
// draw nothing if the distance between grid lines would be less than 1px
// otherwise the grid lines would completely cover the whole map
if (mGridIntervalY / mapSettings.mapUnitsPerPixel() < 1)
return 1;
const QPolygonF& canvasPoly = canvasPolygon(mapCanvas);
const QPolygonF& mapPolygon = canvasExtent(mapCanvas);
const QRectF& mapBoundingRect = mapPolygon.boundingRect();
QLineF lineEast(mapPolygon[2], mapPolygon[1]);
QLineF lineWest(mapPolygon[3], mapPolygon[0]);
double len = lineEast.length();
Q_ASSERT(fabs(len - lineWest.length()) < 1e-6); // no shear
double roundCorrection = mapBoundingRect.top() > 0 ? 1.0 : 0.0;
double dist = (int)((mapBoundingRect.top() - mGridOffsetY) / mGridIntervalY + roundCorrection) * mGridIntervalY + mGridOffsetY;
dist = dist - mapBoundingRect.top();
while (dist < len) {
double t = dist / len;
QPointF p0 = lineWest.pointAt(t);
QPointF p1 = lineEast.pointAt(t);
QLineF line(p0, p1);
clipByRect(line, canvasPoly);
line = QLineF(m2p.transform(QgsPoint(line.pointAt(0))).toQPointF(), m2p.transform(QgsPoint(line.pointAt(1))).toQPointF());
lines.push_back(qMakePair(p0.y(), line));
dist += mGridIntervalY;
}
return 0;
}
int QgsDecorationGrid::yGridLines(QList< QPair< qreal, QLineF > >& lines) const {
// prepare vertical lines
lines.clear();
if (mGridIntervalX <= 0.0) {
return 1;
}
const QgsMapCanvas& mapCanvass = *mapCanvas;
const QgsMapSettings& mapSettings = mapCanvass.mapSettings();
const QgsMapToPixel& m2p = mapSettings.mapToPixel();
// draw nothing if the distance between grid lines would be less than 1px
// otherwise the grid lines would completely cover the whole map
if (mGridIntervalX / mapSettings.mapUnitsPerPixel() < 1)
return 1;
const QPolygonF& canvasPoly = canvasPolygon(mapCanvas);
const QPolygonF& mapPolygon = canvasExtent(mapCanvas);
QLineF lineSouth(mapPolygon[3], mapPolygon[2]);
QLineF lineNorth(mapPolygon[0], mapPolygon[1]);
double len = lineSouth.length();
Q_ASSERT(fabs(len - lineNorth.length()) < 1e-6); // no shear
const QRectF& mapBoundingRect = mapPolygon.boundingRect();
double roundCorrection = mapBoundingRect.left() > 0 ? 1.0 : 0.0;
double dist = (int)((mapBoundingRect.left() - mGridOffsetX) / mGridIntervalX + roundCorrection) * mGridIntervalX + mGridOffsetX;
dist = dist - mapBoundingRect.left();
while (dist < len) {
double t = dist / len;
QPointF p0(lineNorth.pointAt(t));
QPointF p1(lineSouth.pointAt(t));
QLineF line(p0, p1);
clipByRect(line, canvasPoly);
line = QLineF(m2p.transform(QgsPoint(line.pointAt(0))).toQPointF(), m2p.transform(QgsPoint(line.pointAt(1))).toQPointF());
lines.push_back(qMakePair(p0.x(), line));
dist += mGridIntervalX;
}
return 0;
}
QgsDecorationGrid::Border QgsDecorationGrid::borderForLineCoord(const QPointF& point, QPainter* p) const {
if (point.x() <= mGridPen.widthF()) {
return Left;
} else if (point.x() >= (p->device()->width() - mGridPen.widthF())) {
return Right;
} else if (point.y() <= mGridPen.widthF()) {
return Top;
} else {
return Bottom;
}
}
void QgsDecorationGrid::drawText(QPainter* p, double x, double y, const QString& text, const QFont& font) const {
QFont textFont = scaledFontPixelSize(font);
p->save();
p->setFont(textFont);
p->setPen(mGridPen);
double scaleFactor = 1.0 / FONT_WORKAROUND_SCALE;
p->scale(scaleFactor, scaleFactor);
p->drawText(QPointF(x * FONT_WORKAROUND_SCALE, y * FONT_WORKAROUND_SCALE), text);
p->restore();
}
void QgsDecorationGrid::drawText(QPainter* p, const QRectF& rect, const QString& text, const QFont& font, Qt::AlignmentFlag halignment, Qt::AlignmentFlag valignment) const {
QFont textFont = scaledFontPixelSize(font);
QRectF scaledRect(rect.x() * FONT_WORKAROUND_SCALE, rect.y() * FONT_WORKAROUND_SCALE,
rect.width() * FONT_WORKAROUND_SCALE, rect.height() * FONT_WORKAROUND_SCALE);
p->save();
p->setFont(textFont);
double scaleFactor = 1.0 / FONT_WORKAROUND_SCALE;
p->scale(scaleFactor, scaleFactor);
p->drawText(scaledRect, halignment | valignment | Qt::TextWordWrap, text);
p->restore();
}
double QgsDecorationGrid::textWidthMillimeters(const QFont& font, const QString& text) const {
QFont metricsFont = scaledFontPixelSize(font);
QFontMetrics fontMetrics(metricsFont);
return (fontMetrics.width(text) / FONT_WORKAROUND_SCALE);
}
double QgsDecorationGrid::fontHeightCharacterMM(const QFont& font, const QChar& c) const {
QFont metricsFont = scaledFontPixelSize(font);
QFontMetricsF fontMetrics(metricsFont);
return (fontMetrics.boundingRect(c).height() / FONT_WORKAROUND_SCALE);
}
double QgsDecorationGrid::fontAscentMillimeters(const QFont& font) const {
QFont metricsFont = scaledFontPixelSize(font);
QFontMetricsF fontMetrics(metricsFont);
return (fontMetrics.ascent() / FONT_WORKAROUND_SCALE);
}
double QgsDecorationGrid::pixelFontSize(double pointSize) const {
return pointSize;
}
QFont QgsDecorationGrid::scaledFontPixelSize(const QFont& font) const {
QFont scaledFont = font;
double pixelSize = pixelFontSize(font.pointSizeF()) * FONT_WORKAROUND_SCALE + 0.5;
scaledFont.setPixelSize(pixelSize);
return scaledFont;
}
void QgsDecorationGrid::checkMapUnitsChanged() {
// disable grid if units changed and grid is enabled, and update the canvas
// this is to avoid problems when CRS changes to/from geographic and projected
// a better solution would be to change the grid interval, but this is a little tricky
// note: we could be less picky (e.g. from degrees to DMS)
QGis::UnitType mapUnits = mapCanvas->mapSettings().mapUnits();
if (mEnabled && (mMapUnits != mapUnits)) {
mEnabled = false;
mMapUnits = QGis::UnknownUnit; // make sure isDirty() returns true
if (!mapCanvas->isFrozen()) {
update();
}
}
}
bool QgsDecorationGrid::isDirty() {
// checks if stored map units is undefined or different from canvas map units
// or if interval is 0
if (mMapUnits == QGis::UnknownUnit ||
mMapUnits != mapCanvas->mapSettings().mapUnits() ||
mGridIntervalX == 0 || mGridIntervalY == 0)
return true;
return false;
}
void QgsDecorationGrid::setDirty(bool dirty) {
if (dirty) {
mMapUnits = QGis::UnknownUnit;
} else {
mMapUnits = mapCanvas->mapSettings().mapUnits();
}
}
bool QgsDecorationGrid::getIntervalFromExtent(double* values, bool useXAxis) {
// get default interval from current extents
// calculate a default interval that is approx (extent width)/5, adjusted so that it is a rounded number
// e.g. 12.7 -> 10 66556 -> 70000
double interval = 0;
QgsRectangle extent = mapCanvas->extent();
if (useXAxis)
interval = (extent.xMaximum() - extent.xMinimum()) / 5;
else
interval = (extent.yMaximum() - extent.yMinimum()) / 5;
QgsDebugMsg(QString("interval: %1").arg(interval));
if (interval != 0) {
double interval2 = 0;
int factor = pow(10, floor(log10(interval)));
if (factor != 0) {
interval2 = qRound(interval / factor) * factor;
QgsDebugMsg(QString("interval2: %1").arg(interval2));
if (interval2 != 0)
interval = interval2;
}
}
values[0] = values[1] = interval;
values[2] = values[3] = 0;
return true;
}
bool QgsDecorationGrid::getIntervalFromCurrentLayer(double* values) {
// get current layer and make sure it is a raster layer and CRSs match
QgsMapLayer* layer = mapCanvas->currentLayer();
if (!layer) {
QMessageBox::warning(0, tr("Error"), tr("No active layer"));
return false;
}
if (layer->type() != QgsMapLayer::RasterLayer) {
QMessageBox::warning(0, tr("Error"), tr("Please select a raster layer"));
return false;
}
QgsRasterLayer* rlayer = dynamic_cast<QgsRasterLayer*>(layer);
if (!rlayer || rlayer->width() == 0 || rlayer->height() == 0) {
QMessageBox::warning(0, tr("Error"), tr("Invalid raster layer"));
return false;
}
const QgsCoordinateReferenceSystem& layerCRS = layer->crs();
const QgsCoordinateReferenceSystem& mapCRS =
mapCanvas->mapSettings().destinationCrs();
// is this the best way to compare CRS? should we also make sure map has OTF enabled?
// TODO calculate transformed values if necessary
if (layerCRS != mapCRS) {
QMessageBox::warning(0, tr("Error"), tr("Layer CRS must be equal to project CRS"));
return false;
}
// calculate interval
// TODO add a function in QgsRasterLayer to get x/y resolution from provider,
// because this might not be 100% accurate
QgsRectangle extent = rlayer->extent();
values[0] = fabs(extent.xMaximum() - extent.xMinimum()) / rlayer->width();
values[1] = fabs(extent.yMaximum() - extent.yMinimum()) / rlayer->height();
// calculate offset - when using very high resolution rasters in geographic CRS
// there seems to be a small shift, but this may be due to rendering issues and depends on zoom
double ratio = extent.xMinimum() / values[0];
values[2] = (ratio - floor(ratio)) * values[0];
ratio = extent.yMinimum() / values[1];
values[3] = (ratio - floor(ratio)) * values[1];
QgsDebugMsg(QString("xmax: %1 xmin: %2 width: %3 xInterval: %4 xOffset: %5").arg(
extent.xMaximum()).arg(extent.xMinimum()).arg(rlayer->width()).arg(values[0]).arg(values[2]));
QgsDebugMsg(QString("ymax: %1 ymin: %2 height: %3 yInterval: %4 yOffset: %5").arg(
extent.yMaximum()).arg(extent.yMinimum()).arg(rlayer->height()).arg(values[1]).arg(values[3]));
return true;
}
定义一个经纬网的封装类:
#pragma once
#include <QObject>
#include <QPen>
#include <QFont>
class QgsDecorationGrid;
class QgsMapCanvas;
class QPainter;
#define QtMapGridInst() (QtMapGrid::getInstance())
class QtMapGrid : public QObject {
Q_OBJECT
private:
QtMapGrid(QObject *parent = nullptr);
static QtMapGrid* _instance;
public:
~QtMapGrid();
static QtMapGrid* getInstance();
void init(QgsMapCanvas* canvas);
void showGrid(bool show);
private slots:
void renderDecorationItems(QPainter *p);
private:
QgsDecorationGrid * _map2DGrid = nullptr;
bool _isShow = false;//是否显示
QColor _gridColor = QColor(222, 155, 67);
double _gridWidth = 0.5;
QFont _gridFont;
};
#include <QPainter>
#include "QtMapGrid.h"
#include "qgsmapcanvas.h"
#include "qgssymbolv2.h"
#include "QgsDecorationGrid.h"
QtMapGrid* QtMapGrid::_instance = new QtMapGrid;
QtMapGrid::QtMapGrid(QObject *parent)
: QObject(parent) {
_gridFont.setFamily(QStringLiteral("微软雅黑"));
_gridFont.setBold(true);
_gridFont.setItalic(true);
_gridFont.setPointSize(20);
}
QtMapGrid::~QtMapGrid() {
}
QtMapGrid* QtMapGrid::getInstance() {
return _instance;
}
void QtMapGrid::init(QgsMapCanvas* canvas) {
_map2DGrid = new QgsDecorationGrid(nullptr, canvas);
connect(canvas, SIGNAL(renderComplete(QPainter *)), this, SLOT(renderDecorationItems(QPainter *)));
}
void QtMapGrid::showGrid(bool show) {
_isShow = show;
_map2DGrid->setEnabled(_isShow);
_map2DGrid->setGridIntervalX(5.0);
_map2DGrid->setGridIntervalY(5.0);
//设置线型
QgsLineSymbolV2* lineGrid =const_cast<QgsLineSymbolV2*>(_map2DGrid->lineSymbol());
lineGrid->setColor(_gridColor);
lineGrid->setWidth(_gridWidth);
lineGrid->setAlpha(0.5);
//设置字体
_map2DGrid->setGridPenColor(QColor(0, 0, 255, 150));
_map2DGrid->setGridAnnotationFont(_gridFont);
_map2DGrid->setShowGridAnnotation(true);
_map2DGrid->run();
}
void QtMapGrid::renderDecorationItems(QPainter *p) {
_map2DGrid->render(p);
}
调用:
QtMapGridInst()->showGrid(/*false*/true);
aaa