Qt将GeoJson文件转为mif文件的示例

15 篇文章 2 订阅

目录

理论

示例

1分析

2代码


本文以geojson转mif文件为例,介绍如何使用qt读取json文件。整个工程上传在我的资源Qt程序,将三沙市的GeoJson格式数据转化为mif文件-C++文档类资源-CSDN下载

理论

Json的两种基本数据类型是对象(object)和数组(array)。Json格式的对象用{}标识,而数组用[]标识。
Qt提供三种数据类型来描述Json: QJsonObject QJsonArray QJsonValue。前两者分别代表对象和数组,第三个QJsonValue的使用场景如下:
考虑一个典型的Json对象 {"x": "1"}
你可以用QJsonObject::value("x")来索引"1"。而value这个函数的返回值类型就是QJsonValue。你可以通过QJsonValue::toString()进一步将这个返回值变为字符串型。
考虑一个典型的Json对象 {"x": {"1", "t"}}
你可以用QJsonObject::value("x")来索引{"1","t"}。而value这个函数的返回值类型还是QJsonValue。你可以通过QJsonValue::toObject()进一步将这个返回值变为{"1","t"}实际代表的QJsonObject型。
考虑一个典型的Json对象 {"x": ["1", "t"]}。
你可以用QJsonObject::value("x")来索引["1","t"]。而value这个函数的返回值类型还是QJsonValue。你可以通过QJsonValue::toArray()进一步将这个返回值变为["1","t"]实际代表的QJsonArray型。
总结起来说,当你从一个文本文件里读取json时,返回的结果类型是QJsonObject。当你通过value函数从QJsonObject类型的变量中获取结果时,结果类型为QJsonValue;
当你通过at函数从QJsonArray类型的变量中获取结果时,结果类型也为QJsonValue。而QJsonValue可以向QJsonObject和QJsonArray转化。
接下来通过一个示例展示Qt读取GeoJson文件的操作。

示例


1分析


GeoJson本质就是一个含有地理信息的Json文件。但是不同的信息源产生的GeoJson格式略有不同。关于GeoJson的描述可参考GeoJSON结构解析_GADFLYGIS的博客-CSDN博客,这里不赘述。以高德-阿里联合发布的数据为例https://datav.aliyun.com/portal/school/atlas/area_selector,其格式如下:

{
	"type":"....",
	"features":[
			{
				"type":"...", 
		 		"properties":{...}, 
			 	"geometry": {
						"type":"MultiPolygon",
						"coordinates": [
								[[[x,y],[..],[...]]],	
								[[[x,y],[..],[...]]],	 
								.......
							        ]
				}
			},

			{		
			},

			{
			},

			.....
		    ]
}

根节点下面有若干成员,其中最重要的是features。features索引了一个数组。每个数组的成员又是一个json对象。一般来说,这个对象的成员里面最重要的是geometry项。其type指明了几何形状的类型。"type":"MultiPolygon",表明几何形状由多个多边形组成。coordinates存储每个多边形的定点经纬度。这里有个反直觉的地方:每个顶点经纬度构成一个数组,需要一重方括号;同一个多边形包含多个顶点,也需要一重方括号;多个多边形又构成一个数组,又需要一重方括号。所以coordinates总共需要三重括号。但是在实际数据文件中,多边形用两重括号包括了顶点数组。所以实际上coordinates总共用了四重括号。

 

2代码


根据以上分析,我写了一个Qt程序来将GeoJson格式的南海诸岛地理信息转化为mif格式。
读取json文件

QFile fp("SouthSea.json");
    if(fp.open(QIODevice::ReadOnly))
    {
        QJsonObject jsonObj = jsonFromQba(fp.readAll());
        fp.close();
        QJsonArray jsonArr = jsonObj.value("features").toArray();
        std::list<QPolygonF> lstIslands = lstGetPolygon(jsonArr);
        vWriteMifFile(lstIslands);
    }

如同前面分析的,根节点是一个对象object。 jsonObj.value("features")获取对象的成员,并将其转化为QJsonArray。
随后函数lstGetPolygon将QJsonArray转化为一个QPolygonF的集合。

std::list<QPolygonF> lstGetPolygon(const QJsonArray & jsonArr)
{
    std::list<QPolygonF> lstRet;
    for(const auto & jsonVal: jsonArr)
    {
		QJsonValue jsonGeom = jsonVal.toObject().value("geometry");
		if (jsonGeom.isObject())
		 {
			 QJsonValue jsonValCood = jsonGeom.toObject().value("coordinates");
			 QJsonArray jsonArrCoord = jsonValCood.toArray();
			 std::list<QPolygonF> lstPloygon = lstPolygonFromArray(jsonArrCoord);
			 lstRet.splice(lstRet.end(), lstPloygon);
		 }
    }

    return lstRet;
}

由于features索引了一个数组,所以要通过 for(const auto & jsonVal: jsonArr)遍历其所有元素。
数组的每一个元素格式都是{ "type": "Feature", "properties": {}, "geometry": {},......}。为了提取经纬度信息,接下来采用value("geometry")函数返回QJsonValue,并利用函数将toObject()转化为QJsonObject。然后获取坐标"coordinates"。

"coordinates": [
    [[[x,y],[..],[...]]],    
    [[[x,y],[..],[...]]],
    .......]


coordinates是数组,所以采用函数toArray()将其转化为QJsonArray,随后利用函数lstPolygonFromArray(接下来会介绍)返回QPolygonF的集合。由于features索引的数组中含有多个元素,以上只是处理了其中一个元素,所以还要用splice函数把每个元素对应的集合拼接到同一个集合lstRet中,作为返回值。

//下面的函数将geoJson的coordinates的坐标解析出来
std::list<QPolygonF> lstPolygonFromArray(const QJsonArray & jsonArr)
{
    std::list<QPolygonF> lstRet;
    //jsonArr是多元数组元素,每一个元素是一个PolygonF
    for(const auto & jsonVal: jsonArr)
    {
        auto jsonPolygon = jsonVal.toArray();
        if(1 == jsonPolygon.size())
        {
            QPolygonF plgn;
            auto jsonLine = jsonPolygon.at(0).toArray();
            for(const auto & jsonVertex: jsonLine)
            {
                auto jsonLongLat = jsonVertex.toArray();
                if(jsonLongLat.size() == 2)
                    plgn<<QPointF(jsonLongLat.at(0).toDouble(), jsonLongLat.at(1).toDouble());
            }

            lstRet.push_back(plgn);
        }
    }

    return lstRet;
}

lstPolygonFromArray的输入参数jsonArr是一个数组类型。所以要用for(const auto & jsonVal: jsonArr)遍历该数组。
每个数组的元素又是一个数组,如下形式:

[[[x,y],[..],[...]]]


正如前面所说,“在实际数据文件中,多边形用两重括号包括了顶点数组”,所以对于jsonArr的每个元素来说,第一重括号内只有一个元素。要想“拨开”第一层括号,只要用at(0)即可。随后用for(const auto & jsonVertex: jsonLine)遍历第二层括号。括号内是一个个二元数组jsonLongLat 。于是用jsonLongLat.at(0)和jsonLongLat.at(1)获取二元数组的经纬度,并将经纬度作为顶点信息存入多边形plgn。当一个多边形(也即是[[[x,y],[..],[...]])被遍历完毕后,将plgn放入lstRet。本函数遍历完毕后,完成了对一个"coordinates"的解析。"features"索引一个数组,数组的每个元素都含有一个"coordinates",所以一个GeoJson文件将对"features"的所有"coordinates"进行处理,返回的是若干多边形的集合。

void vWriteMifFile(const std::list<QPolygonF> & lstPolygon)
{
    QFile fp("SouthSea.mif");
    if(fp.open(QFile::WriteOnly))
    {
        QString qstrRegion = QString("Region  %1\n").arg(lstPolygon.size());
        fp.write(qstrRegion.toLatin1());
        for(const auto & polygon: lstPolygon)
        {
            QString qstrPoints = QString("  %1\n").arg(polygon.size());
            fp.write(qstrPoints.toLatin1());
            for(const auto & pnt: polygon)
            {
                int iLong = pnt.x() * 1000000;
                int iLat = pnt.y() * 1000000;
                QString qstrPnt =
                        QString("%1.%2 %3.%4\n").
                        arg(iLong/1000000).arg(iLong%1000000, 6, 10, QChar('0')).
						arg(iLat / 1000000).arg(iLat % 1000000, 6, 10, QChar('0'));
	fp.write(qstrPnt.toLatin1());
            }
        }

        fp.close();
    }
}

vWriteMifFile函数产生mif文件。
mif 文件的第一行是以"Region"开头。所谓region就是刚才说的多边形。region后面是多边形的个数。随后是逐个多边形的经纬度信息。
每个多边形的信息的第一行是其顶点总数,接下来是各个顶点经纬度。下图是一个mif文件的开头部分截图。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值