QGIS在地图上绘制经纬网

16 篇文章 5 订阅

利用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 &lt;
		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

  • 3
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 15
    评论
评论 15
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

wb175208

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值