GeoJson Viewer (QML)
GeoJson Viewer 示例演示了如何操作 MapItems,处理用户的输入输出以及与 GeoJson 文件的交互。
示例显示了一个包含各种 MapItems 的地图。MapItems 可以通过使用 QtLocation 的 GeoJsonData API 从 GeoJson 文件导入,或者通过用户使用 TapHandlers 绘制。
GeoJson 文件的示例可以在示例目录中的 data 目录找到。
要绘制 MapItem,在地图的空白部分右键单击并从出现的菜单中选择您所需的项类型。接下来的点击将定义选定的项。示例允许绘制 MapCircles、MapRectangles、MapPolygons 和 MapPolylines。完全由两个点定义的项,例如圆形和矩形,可以通过鼠标左键两次点击来绘制。定义为多个点的项,例如多边形和折线,可以通过任意数量的鼠标左键点击来创建,并通过鼠标右键完成。以这种方式绘制的项将保存为点、多边形和折线以符合 GeoJson 规范,请参阅 https://geojson.org/。
运行示例
要从 Qt Creator 运行示例,打开 欢迎 模式并从 示例 中选择示例。有关更多信息,请访问 构建和运行示例。
创建 MapView
首先,我们创建一个基础地图,所有项都可以放置在其中。我们利用一个 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 模型 / 显示 MapItems
为了在地图上显示文件内容,我们将使用一种称为 Model/View 编程 的设计模式。首先,我们需要设置一个合适的视图,在这个例子中是一个 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
在启动时被加载作为示例。
最后我们需要一个代表,将模型数据转换为项目表示,填充MapItemView。
delegate: GeoJsonDelegate {}
GeoJsonDelegate
元素在文件GeoJsonDelegate.qml
中声明。它是一个DelegateChooser元素,用于考虑不同几何类型的多样特性。
DelegateChooser { id: dc role: "type" }
DelegateChooser包含每个在GeoJson文件中可以找到的几何类型的一个DelegateChoice。属性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文件中读取。然而,这并不是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信号暂时更新MapItem,为用户提供预览。
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) } }
Mapitems由定义在单独的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对象的reset函数。
function clearAllItems() { geoDatabase.clear(); }
© 2024 Qt公司。本文档的贡献权归各自的拥有者所有。所提供的文档根据Free Software Foundation发布、根据GNU自由文档许可证第1.3版本条款授权使用。Qt及其相关标志是芬兰及其它国家的Qt公司的商标。所有其他商标均为其各自所有者的财产。