实验任务
解析法计算任意地块的面积,是地籍测量的基础性工作。通过计算机高级语言编程,试写一段计算面积的程序源代码。
程序界面
程序功能
输入方式
-
通过导入外部文件,外部文件格式如图1所示。
-
还可如图2所示通过导入底图描摹获取。图中的线条可以通过鼠标点击绘制。
查看信息
- 程序可以查看各个点的坐标,如图4所示
2.程序中可以查看这块土地的属性,即:从原始文件中读取地号、权利人、土地坐落、土地用途等必要信息,如图5所示。
程序计算
程序可以根据解析法计算土地的周长与面积,如图6所示。
文件导出
程序可以导出土地的属性信息、坐标点以及计算所得的周长和面积,方法是点击“开始”->“导出文件”,弹出如图7所示对话框,选择导出文件即可。最终结果保存形式如图8所示。
概要设计
在本次程序编写中,有如下问题需要考虑
- 外业测量的数据并不是首尾相连的,所以在画图和计算前必须将第一个点的数据追加到最后,实现相连;
- 外部导入的数据必须考虑缩放因子与平移因子;
X_max = points[0].first;
Y_max = points[0].second;
X_min = points[0].first;
Y_min = points[0].second;
for (; it2 != points.end(); it2++) {
if (it2->first > X_max)
X_max = it2->first;
if (it2->first < X_min)
X_min = it2->first;
if (it2->second > Y_max)
Y_max = it2->second;
if (it2->second < Y_min)
Y_min = it2->second;
}
X_paint = 351;
Y_paint = 480;
dx = (X_max - X_min) / X_paint;
dy = (Y_max - Y_min) / Y_paint;
mx = X_min-15 ;
my = Y_min-15 ;
- 因为涉及中文读取、显示与输出,所以需要考虑中文编码问题,防止出现乱码;
- 老师给的面积计算公式要求第一个点必须是原点,如果不是原点需要在计算面积前进行改化。
for (int i = 0; i < pts.size(); i++) {
pts[i].first -= points[0].first;
pts[i].second -= points[0].second;
}
- 对于本软件要求,还需考虑外部文件的格式与包含内容,在阅读学习了地籍测量方面的规范后,添加了几项必要属性信息。
详细设计
1.根据解析法计算土地面积公式,编写如下的静态库。
template<typename T>
class Area
{
public:
T CaculateSquare(vector<pair<T, T>> &points);
T CaculateCircumference(vector<pair<T, T>> &points);
};
template<typename T>
T Area<T>::CaculateSquare(vector<pair<T, T>>& points)
{
T squre = 0.0;
vector<pair<T, T>> pts = points;
for (int i = 0; i < pts.size(); i++) {
pts[i].first -= points[0].first;
pts[i].second -= points[0].second;
}
for (int i = 0; i < points.size() - 1; i++) {
squre += abs(1.0 / 2 * (pts[i].first*pts[i + 1].second - pts[i + 1].first*pts[i].second));
}
return squre;
}
template<typename T>
T Area<T>::CaculateCircumference(vector<pair<T, T>>& points)
{
T circum = 0.0;
for (int i = 0; i < points.size() - 1; i++) {
circum += sqrt(pow((points[i].first - points[i + 1].first), 2) + pow((points[i].second - points[i + 1].second), 2));
}
return circum;
编写自定义绘图控件,如下为绘图类的定义与方法定义。
namespace Ui {
class PaintWidget;
}
class PaintWidget : public QWidget
{
Q_OBJECT
public:
PaintWidget(QWidget *parent = nullptr);
void mousePressEvent(QMouseEvent *);
void paintEvent(QPaintEvent *event);//绘制图像
void mouseMoveEvent(QMouseEvent *e);//当鼠标移动后实时刷新画布
void PaintWidget::paintClean();//清空画面
~PaintWidget();
QVector<QPointF> pointList;
QVector<QLineF> linefList;
protected:
//void paintEvent(QPaintEvent *event);
bool bDraw; //是否处于绘制状态
bool bLeftClick; //是否已经开始左键点击,同时标识是否开始进行绘制
bool bMove; //是否处于绘制时的鼠标移动状态
QPointF movePoint;
private:
Ui::PaintWidget *ui;
QPoint startPos;
QPoint endPos;
QPixmap *pix;
signals:
void singalDrawOver();
};
编写主窗口的实现类。以下是主窗口的类定义与方法定义。
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
std::vector<std::pair<std::string, std::string>> heads;
std::vector<std::pair<double, double>>points;
QString filename;
double square=0, circumference=0;
public slots:
//用于计算面积周长
void btnClicked();
//用于打开文本文件
void actionTriggered();
//用于加载底图
void actionTriggered01();
//用于清除绘图
void btn_clearClicked();
//用于查看各点信息
void btn_watchClicked();
//用于把周长、面积、土地信息导出来
void actionTriggered02();
private:
Ui::MainWindow *ui;
//用于把文件中的点画出来
void paintOutPoint();
};
编写主函数,用于启动界面
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
QFile qssFile(":style.qss");
qssFile.open(QFile::ReadOnly);
QString qss;
if (qssFile.isOpen())
{
qss = QLatin1String(qssFile.readAll());
qApp->setStyleSheet(qss);
qssFile.close();
}
w.show();
return a.exec();
}
附:计算土地面积周长源代码(C++)
main.cpp
#include "MainWindow.h"
#include <QApplication>
#include<QFile>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
QFile qssFile(":style.qss");
qssFile.open(QFile::ReadOnly);
QString qss;
if (qssFile.isOpen())
{
qss = QLatin1String(qssFile.readAll());
qApp->setStyleSheet(qss);
qssFile.close();
}
w.show();
return a.exec();
}
mathlib/Area.hpp
template<typename T>
class Area
{
public:
T CaculateSquare(vector<pair<T, T>> &points);
T CaculateCircumference(vector<pair<T, T>> &points);
};
template<typename T>
T Area<T>::CaculateSquare(vector<pair<T, T>>& points)
{
T squre = 0.0;
vector<pair<T, T>> pts = points;
for (int i = 0; i < pts.size(); i++) {
pts[i].first -= points[0].first;
pts[i].second -= points[0].second;
}
for (int i = 0; i < points.size() - 1; i++) {
squre += abs(1.0 / 2 * (pts[i].first*pts[i + 1].second - pts[i + 1].first*pts[i].second));
}
return squre;
}
template<typename T>
T Area<T>::CaculateCircumference(vector<pair<T, T>>& points)
{
T circum = 0.0;
for (int i = 0; i < points.size() - 1; i++) {
circum += sqrt(pow((points[i].first - points[i + 1].first), 2) + pow((points[i].second - points[i + 1].second), 2));
}
return circum;
}
PaintWidget.h
#ifndef PAINTWIDGET_H
#define PAINTWIDGET_H
#include <QWidget>
#include<QMouseEvent>
#include<QPaintEvent>
#include<qdebug.h>
namespace Ui {
class PaintWidget;
}
class PaintWidget : public QWidget
{
Q_OBJECT
public:
PaintWidget(QWidget *parent = nullptr);
void mousePressEvent(QMouseEvent *);
void paintEvent(QPaintEvent *event);//绘制图像
void mouseMoveEvent(QMouseEvent *e);//当鼠标移动后实时刷新画布
void PaintWidget::paintClean();//清空画面
~PaintWidget();
QVector<QPointF> pointList;
QVector<QLineF> linefList;
protected:
//void paintEvent(QPaintEvent *event);
bool bDraw; //是否处于绘制状态
bool bLeftClick; //是否已经开始左键点击,同时标识是否开始进行绘制
bool bMove; //是否处于绘制时的鼠标移动状态
QPointF movePoint;
private:
Ui::PaintWidget *ui;
QPoint startPos;
QPoint endPos;
QPixmap *pix;
signals:
void singalDrawOver();
};
#endif // PAINTWIDGET_H
PaintWidget.cpp
#include "PaintWidget.h"
#include "ui_PaintWidget.h"
#include<QPainter>
#include<iostream>
using namespace std;
PaintWidget::PaintWidget(QWidget *parent) :
QWidget(parent),
ui(new Ui::PaintWidget)
{
ui->setupUi(this);
//填充背景色
setAutoFillBackground(true);
setBackgroundRole(QPalette::Base);
setMouseTracking(true);
}
PaintWidget::~PaintWidget()
{
delete ui;
}
void PaintWidget::mousePressEvent(QMouseEvent *e) {
pointList.push_back(QPointF(e->x(), e->y()));
}
//清空图上点
void PaintWidget::paintClean() {
pointList.clear();
linefList.clear();
update();
}
void PaintWidget::paintEvent(QPaintEvent * event)
{
QPainter painter(this);
int num = pointList.size();
for (int i = 0; i < num; i++) {
painter.drawEllipse(QPoint(pointList[i].x(), pointList[i].y()), 1, 1);
}
if (num > 1) {
for (int i = 0; i < num - 1; i++) {
painter.drawLine(QPoint(pointList[i].x(), pointList[i].y()), QPoint(pointList[i + 1].x(), pointList[i + 1].y()));
}
}
}
void PaintWidget::mouseMoveEvent(QMouseEvent * e)
{
update();
}
MainWindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
//#include "PaintWidget.h"
#include<QtGui>
#include<vector>
#include<string>
#include<qpoint.h>
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
std::vector<std::pair<std::string, std::string>> heads;
std::vector<std::pair<double, double>>points;
QString filename;
double square=0, circumference=0;
public slots:
//用于计算面积周长
void btnClicked();
//用于打开文本文件
void actionTriggered();
//用于加载底图
void actionTriggered01();
//用于清除绘图
void btn_clearClicked();
//用于查看各点信息
void btn_watchClicked();
//用于把周长、面积、土地信息导出来
void actionTriggered02();
private:
Ui::MainWindow *ui;
//用于把文件中的点画出来
void paintOutPoint();
};
#endif // MAINWINDOW_H
MainWindow.cpp
#include "MainWindow.h"
#include "ui_MainWindow.h"
#include "SXWidget.h"
#include<mathlib/AreaAndRound.h>
#include<qfiledialog.h>
#include<iostream>
#include<fstream>
#include<sstream>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
//ui->PaintWidget->setStyleSheet("QWidget{background:white}");
//ui->PaintWidget->ch
//计算按钮
connect(ui->btn, SIGNAL(clicked()), this, SLOT(btnClicked()));
//打开文件按钮
connect(ui->action01, SIGNAL(triggered()), this, SLOT(actionTriggered()));
//清空图层按钮
connect(ui->btn_clear, SIGNAL(clicked()), this, SLOT(btn_clearClicked()));
//查看列表按钮
connect(ui->btn_watch, SIGNAL(clicked()), this, SLOT(btn_watchClicked()));
//打开底图按钮
connect(ui->action02, SIGNAL(triggered()), this, SLOT(actionTriggered01()));
//导出文件
connect(ui->action03, SIGNAL(triggered()), this, SLOT(actionTriggered02()));
}
MainWindow::~MainWindow()
{
delete ui;
}
#include<qdebug.h>
void MainWindow::actionTriggered()
{
//把文件的头部信息与点位信息写入内存
filename= QFileDialog::getOpenFileName(this, QString::fromLocal8Bit("打开"), "", tr("(*.txt)"));
//filename= "C:\\Users\\sishu\\Desktop\\temperary\\input.txt";
qDebug() << filename;
std::ifstream ifs(filename.toStdString());
std::string temp;
std::pair<std::string, std::string> oneHead;
std::pair<double, double> onePoint;
while (std::getline(ifs, temp)) {
if (temp != "END") {
std::istringstream iss(temp);
std::string s1,s2;
iss >> oneHead.first >> oneHead.second;
heads.push_back(oneHead);
}
if (temp == "END") { break; }
}
while (std::getline(ifs, temp)) {
std::istringstream iss(temp);
std::string s1,s2,s3;
iss >> s1 >> s2 >> s3;
onePoint.first = atof(s2.c_str());
onePoint.second = atof(s3.c_str());
points.push_back(onePoint);
}
points.push_back(points[0]);
//头表格
//表头
QStandardItemModel* model = new QStandardItemModel();
QStringList labels ;
labels << QStringLiteral("属性名") << QStringLiteral("属性值");
model->setHorizontalHeaderLabels(labels);
QStandardItem *item = nullptr;
for (int i = 0; i < heads.size(); i++) {
item = new QStandardItem(QString::fromStdString(heads[i].first));
model->setItem(i, 0, item);
item = new QStandardItem(QString::fromStdString(heads[i].second));
model->setItem(i, 1, item);
}
ui->tableView_2->setModel(model);
paintOutPoint();
}
void MainWindow::actionTriggered01()
{
QString filename2;
filename2 = QFileDialog::getOpenFileName(this, QString::fromLocal8Bit("打开"), "", tr("(*.jpg)"));
ui->myWidget->style();
/*SXWidget *sxwidget = new SXWidget;
sxwidget->show();*/
}
void MainWindow::btn_clearClicked()
{
ui->myWidget->paintClean();
}
void MainWindow::btn_watchClicked()
{
if (points.size()==0) {
double dx, dy, mx, my;
dx = dy = 1.0125;
mx = my = 2.3235;
std::pair<double, double> onePoint;
for (int i = 0; i < ui->myWidget->pointList.count(); i++) {
onePoint.first = ui->myWidget->pointList[i].x()*dx+mx;
onePoint.second = ui->myWidget->pointList[i].y()*dy+my;
points.push_back(onePoint);
}
}
//表头
QStandardItemModel* model = new QStandardItemModel();
QStringList labels = QObject::trUtf8("id,x,y").simplified().split(",");
model->setHorizontalHeaderLabels(labels);
QStandardItem *item = nullptr;
for (int i = 0; i < points.size(); i++) {
item = new QStandardItem(QString("%1").arg(i));
model->setItem(i, 0, item);
item = new QStandardItem(QString::number(points[i].first));
model->setItem(i, 1, item);
item = new QStandardItem(QString::number(points[i].second));
model->setItem(i, 2, item);
}
ui->tableView->setModel(model);
}
#include<codecvt>
//接口是两个std::string
std::string gb2312_to_utf8(std::string const &strGb2312)
{
std::vector<wchar_t> buff(strGb2312.size());
#ifdef _MSC_VER
std::locale loc("zh-CN");
#else
std::locale loc("zh_CN.GB18030");
#endif
wchar_t* pwszNext = nullptr;
const char* pszNext = nullptr;
mbstate_t state = {};
int res = std::use_facet<std::codecvt<wchar_t, char, mbstate_t> >
(loc).in(state,
strGb2312.data(), strGb2312.data() + strGb2312.size(), pszNext,
buff.data(), buff.data() + buff.size(), pwszNext);
if (std::codecvt_base::ok == res)
{
//转UTF8
std::wstring_convert<std::codecvt_utf8<wchar_t>> cutf8;
return cutf8.to_bytes(std::wstring(buff.data(), pwszNext));
}
return "";
}
std::string qstr2str(const QString qstr)
{
QByteArray cdata = qstr.toLocal8Bit();
return std::string(cdata);
}
///乱码真的难搞
void MainWindow::actionTriggered02()
{
QString out_filename;
QString temp,temp1;
out_filename = QFileDialog::getOpenFileName(this, QString::fromLocal8Bit("打开"), "", tr("(*.txt)"));
std::ofstream ofs(out_filename.toStdString());
std::vector<std::pair<std::string, std::string>>::iterator it = heads.begin();
for (; it != heads.end(); it++) {
//ofs << it->first << " " << it->second<<std::endl;
temp=QString::fromStdString(it->first);
temp1 = QString::fromStdString(it->second);
ofs << qstr2str(temp) << " " << qstr2str(temp1) <<std::endl;
}
ofs << "END" << std::endl;
std::vector<std::pair<double, double>>::iterator its = points.begin();
int i = 0;
for (; its != points.end(); its++) {
ofs <<i<<" " <<its->first << " " << its->second<<std::endl;
i++;
}
ofs << "面积" << " " << ui->areaEdit->text().toStdString() <<std::endl;
ofs << "周长" << " " << ui->roundEdit->text().toStdString() <<std::endl;
}
void MainWindow::paintOutPoint()
{
//首先计算外部点转换到像素点的比例尺
double X_max, Y_max, X_min, Y_min;
int X_paint, Y_paint;
double dx, dy;
double mx, my;
std::vector < std::pair<double, double> > ::iterator it2 = points.begin();
X_max = points[0].first;
Y_max = points[0].second;
X_min = points[0].first;
Y_min = points[0].second;
for (; it2 != points.end(); it2++) {
if (it2->first > X_max)
X_max = it2->first;
if (it2->first < X_min)
X_min = it2->first;
if (it2->second > Y_max)
Y_max = it2->second;
if (it2->second < Y_min)
Y_min = it2->second;
}
X_paint = 351;
Y_paint = 480;
dx = (X_max - X_min) / X_paint;
dy = (Y_max - Y_min) / Y_paint;
mx = X_min-15 ;
my = Y_min-15 ;
QPointF point;
std::vector < std::pair<double, double> > ::iterator it = points.begin();
qDebug() << dx << " " << dy;
for (; it != points.end(); it++) {
point = QPoint(int((it->first-mx)/dx), int((it->second-my)/dy));
ui->myWidget->pointList.push_back(point);
qDebug() << point.x() << " " << point.y() << " ";
}
ui->myWidget->update();
}
void MainWindow::btnClicked() {
Area<double> area;
ui->areaEdit->setText(QString::number(area.CaculateSquare(points),'f',3));
ui->roundEdit->setText(QString::number(area.CaculateCircumference(points),'f',3));
}