GeoJson 查看器(QML)
GeoJson 查看器示例演示了如何操作 MapItems、处理用户输入以及与 GeoJson 文件之间的输入/输出。
该示例显示了一张包含各种 MapItems 的地图。MapItems 可以使用QtLocation 的GeoJsonData API 从 GeoJson 文件导入,也可以使用TapHandlers 由用户绘制。
GeoJson 文件的示例可在示例目录下的 data 目录中找到。
要绘制地图项目,请右键单击地图的空白部分,然后在出现的菜单中选择项目类型。接下来的点击将定义所选项目。本示例允许绘制MapCircles 、MapRectangles 、MapPolygons 和MapPolylines 。完全由两点定义的项目,即圆形和矩形,只需点击两次鼠标左键即可绘制。由多个点定义的项目,即多边形和折线,可通过点击任意次数的鼠标左键来创建,并通过点击鼠标右键来完成。以这种方式绘制的项目将保存为点、多边形和折线,以符合 GeoJson 规范,请参阅https://geojson.org/。
运行示例
运行示例 Qt Creator,打开Welcome 模式,并从Examples 中选择示例。更多信息,请参见Qt Creator: 教程:构建并运行。
创建地图视图
首先,我们创建一个基础地图,所有项目都可以放在上面。我们利用MapView 元素,该元素结合了基本的Map 和输入处理(鼠标滚轮、拖动等)。可通过map 属性访问底层Map 。如果遗漏了MapView 中的某个属性,则很可能可以通过MapView.map 进行访问。
MapView { id: view anchors.fill: parent map.plugin: Plugin { name: "osm" } map.zoomLevel: 4 map.center: QtPositioning.coordinate(3, 8) }
设置 GeoJson 模型/显示地图项目
为了在地图上显示文件内容,我们将使用一种称为模型/视图编程(Model/View Programming)的设计模式。首先,我们需要设置一个合适的视图,在本例中就是一个MapItemView 元素。它的父级必须设置为MapView 的底层地图,以便正确显示放置在其中的所有项目。
MapItemView { id: miv parent: view.map }
接下来,我们需要一个合适的模型,代表一个 GeoJSON 文档。为此,QtLocation 提供了可以读写 GeoJSON 文件的GeoJsonData 元素。可以很容易地将其实例化
GeoJsonData { id: geoDatabase sourceUrl: ":/data/11-full.json" }
并分配给MapItemView 。
model: geoDatabase.model
例如,文件11-full.json
会在启动时加载。
最后,我们需要一个delegate ,将模型数据转换为项目表示,填充MapItemView 。
delegate: GeoJsonDelegate {}
GeoJsonDelegate
元素在文件GeoJsonDelegate.qml
中声明。这是一个DelegateChooser 元素,以考虑不同几何类型的不同属性。
DelegateChooser { id: dc role: "type" }
DelegateChooser 包含每个几何类型的DelegateChoice ,可在 GeoJson 文件中找到。属性role 将与DelegateChoice.roleValue 匹配,以确定正确的委托。
例如,在 GeoJson 中用"type":"Point"
描述的点,由MapItemView 上的MapCircle 表示:
DelegateChoice { roleValue: "Point" delegate: MapCircle { property string geojsonType: "Point" property var props: modelData.properties geoShape: modelData.data radius: (props && props.radius) || 20*1000 border.width: 2 border.color: hh.hovered ? "magenta" : Qt.darker(color) opacity: dc.defaultOpacity color: (props && props.color) || (parent && parent.props && parent.props.color) || dc.defaultColor } }
MapCircle 的属性,如color 或radius ,试图从 GeoJson 文件中读取,该文件以 modelData 属性的形式提供。不过,这并不是 GeoJson 的严格标准,所有属性都会设置回退值。
将 MapItems 写入 GeoJson
要将 MapItems 写入 GeoJson 文件,我们只需使用指定的文件名调用GeoJsonData::saveAs 函数即可。这会将当前模型中的所有项目写入指定文件。任何其他应写入文件的项都必须先使用函数GeoJsonData::addItem 或GeoJsonData::setModelToMapContents 添加到模型中。
geoDatabase.saveAs(fileWriteDialog.selectedFile)
用户与 MapItems 的交互
为了处理用户交互,我们将使用PointHandlers 。与总是覆盖正方形的MouseArea 相反,它们符合底层项的精确形状,因此特别适合这项任务。从 GeoJson 文件导入的 MapItems 会直接在委托中获得自己的HoverHandler 和TapHandler :
TapHandler { onTapped: { if (props !== undefined) console.log(props.name) else if (parent.parent.geojsonType == "MultiPoint") console.log(parent.parent.props.name) else console.log("NO NAME!", props) } } HoverHandler { id: hh }
TapHandler 用于在点击项目时在控制台中写入项目的一些信息。HoverHandler 用于高亮显示鼠标指针下方的项目。这是通过根据HoverHandler 的属性/状态hovered 来描述属性border.color 来实现的。
添加新项目
MapView 结合使用HoverHandler 和TapHandler 可以对用户的鼠标移动和点击做出反应。
如果TapHandler 发出singleTapped 信号,我们将在LeftButton 上创建或修改一个新的 MapItem,并在RightButton 上完成 MapItem。如果没有需要完成的项目,那么RightButton 将打开一个菜单。
onSingleTapped: (eventPoint, button) => { lastCoordinate = view.map.toCoordinate(tapHandler.point.position) if (button === Qt.RightButton) { if (view.unfinishedItem !== undefined) { view.finishGeoItem() } else mapPopupMenu.show(lastCoordinate) } else if (button === Qt.LeftButton) { if (view.unfinishedItem !== undefined) { if (view.unfinishedItem.addGeometry(view.map.toCoordinate(tapHandler.point.position), false)) { view.finishGeoItem() } } } }
pointChanged 信号用于临时更新地图项目,让用户预览。
HoverHandler { id: hoverHandler property variant currentCoordinate grabPermissions: PointerHandler.CanTakeOverFromItems | PointerHandler.CanTakeOverFromHandlersOfDifferentType onPointChanged: { currentCoordinate = view.map.toCoordinate(hoverHandler.point.position) if (view.unfinishedItem !== undefined) view.unfinishedItem.addGeometry(view.map.toCoordinate(hoverHandler.point.position), true) } }
地图项是从单独的 qml 文件中定义的原型生成的。它们通过createComponent 函数创建,并通过addMapItem 添加到地图中。新项目的引用被存储起来,供用户进一步操作。
function addGeoItem(item) { var co = Qt.createComponent('mapitems/'+item+'.qml') if (co.status === Component.Ready) { unfinishedItem = co.createObject(map) unfinishedItem.setGeometry(tapHandler.lastCoordinate) unfinishedItem.addGeometry(hoverHandler.currentCoordinate, false) view.map.addMapItem(unfinishedItem) } else { console.log(item + " is not supported right now, please call us later.") } }
将项目添加到Map 就足以显示该项目。但是,为了进一步使用该项目(例如将其保存到文件中),必须将其添加到模型中。这需要在编辑完成后进行:
function finishGeoItem() { unfinishedItem.finishAddGeometry() geoDatabase.addItem(unfinishedItem) map.removeMapItem(unfinishedItem) unfinishedItem = undefined }
删除项目
要从地图中删除所有项目,我们只需调用GeoJsonData 对象的重置函数即可
function clearAllItems() { geoDatabase.clear(); }
© 2025 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.