KOSMIndoorMap

indoormap.qml
1/*
2 SPDX-FileCopyrightText: 2020 Volker Krause <vkrause@kde.org>
3
4 SPDX-License-Identifier: LGPL-2.0-or-later
5*/
6
7import QtCore
8import QtQuick
9import QtQuick.Layouts
10import QtQuick.Controls as QQC2
11import QtQuick.Dialogs as Dialogs
12import org.kde.kirigami as Kirigami
13import org.kde.kpublictransport as PublicTransport
14import org.kde.kosmindoormap
15import org.kde.kosmindoormap.kpublictransport
16import org.kde.kosmindoorrouting
17import org.kde.osm.editorcontroller
18import org.kde.kirigamiaddons.formcard as FormCard
19
20Kirigami.ApplicationWindow {
21 globalDrawer: Kirigami.GlobalDrawer {
22 title: "Indoor Map"
23 titleIcon: "map-symbolic"
24 isMenu: true
25 actions: [
26 Kirigami.Action {
27 text: "Open O5M File"
28 icon.name: "document-open-symbolic"
29 onTriggered: fileDialog.open()
30 },
31 Kirigami.Action {
32 text: "Open MapCSS Stylesheet"
33 icon.name: "document-open-symbolic"
34 onTriggered: mapcssDialog.open()
35 },
36 Kirigami.Action {
37 text: "Reload Stylesheet"
38 icon.name: "view-refresh-symbolic"
39 shortcut: "F5"
40 onTriggered: {
41 var s = page.map.styleSheet;
42 page.map.styleSheet = "";
43 page.map.styleSheet = s;
44 }
45 },
46 Kirigami.Action { separator: true },
47 Kirigami.Action {
48 text: i18nc("@action", "Data Sources")
49 icon.name: "help-about-symbolic"
50 onTriggered: pageStack.pushDialogLayer(attributionPage, {}, {
51 title: i18nc("@title:window", "Data Sources")
52 })
53 },
54 Kirigami.Action {
55 id: aboutAction
56 text: "About"
57 icon.name: "help-about-symbolic"
58 onTriggered: pageStack.pushDialogLayer(aboutPage, {}, {
59 title: i18nc("@title:window", "About")
60 })
61 }
62 ]
63 }
64 contextDrawer: Kirigami.ContextDrawer {
65 id: contextDrawer
66 }
67
68 Dialogs.FileDialog {
69 id: fileDialog
70 title: "Open OSM File"
71 fileMode: Dialogs.FileDialog.OpenFile
72 nameFilters: ["o5m file (*.o5m)", "OSM XML file (*.osm *.xml)", "PBF file (*.osm.pbf)"]
73 onAccepted: page.map.mapLoader.loadFromFile(fileDialog.selectedFile);
74 }
75 Dialogs.FileDialog {
76 id: mapcssDialog
77 title: "Open MapCSS Stylesheet"
78 fileMode: Dialogs.FileDialog.OpenFile
79 nameFilters: ["MapCSS stylesheet (*.mapcss)"]
80 onAccepted: page.map.styleSheet = mapcssDialog.selectedFile
81 }
82 PublicTransport.Manager { id: ptMgr }
83 Settings {
84 id: settings
85 property alias debugMode: debugAction.checked
86 property alias stylesheet: page.map.styleSheet
87 property alias hoverMode: page.mapHoverEnabled
88 }
89
90 pageStack.initialPage: IndoorMapPage {
91 id: page
92 debug: debugAction.checked
93 routingController: routingController
94
95 actions: [
96 Kirigami.Action {
97 text: "Select Location"
98 icon.name: "search"
99 onTriggered: locationSheet.open()
100 },
101 Kirigami.Action {
102 text: "Light Style"
103 onTriggered: page.map.styleSheet = "breeze-light"
104 },
105 Kirigami.Action {
106 text: "Dark Style"
107 onTriggered: page.map.styleSheet = "breeze-dark"
108 },
109 Kirigami.Action {
110 text: "Diagnostic View"
111 onTriggered: page.map.styleSheet = "diagnostic"
112 },
113 Kirigami.Action {
114 id: debugAction
115 text: "Debug Info Model"
116 checkable: true
117 checked: false
118 },
119 Kirigami.Action {
120 id: platformAction
121 text: "Find Platform"
122 onTriggered: platformSheet.open()
123 visible: !platformModel.isEmpty
124 },
125 Kirigami.Action {
126 id: gateAction
127 text: "Find Gate"
128 onTriggered: gateSheet.open()
129 visible: !gateModel.isEmpty
130 },
131 Kirigami.Action {
132 id: amenityAction
133 text: "Find Amenity"
134 onTriggered: amenitySheet.open()
135 },
136 Kirigami.Action {
137 id: roomAction
138 text: "Find Room"
139 onTriggered: roomSheet.open()
140 },
141 Kirigami.Action {
142 id: equipmentAction
143 text: "Show Elevator Status"
144 checkable: true
145 enabled: !page.map.mapLoader.isLoading
146 onTriggered: page.queryLiveLocationData();
147 },
148 Kirigami.Action {
149 id: rentalVehicleAction
150 text: i18n("Show Rental Vehicles")
151 checkable: true
152 enabled: !page.map.mapLoader.isLoading
153 onTriggered: page.queryLiveLocationData();
154 },
156 text: i18n("Edit with iD")
157 icon.name: "document-edit"
158 onTriggered: EditorController.editBoundingBox(page.map.view.mapSceneToGeo(page.map.view.viewport), Editor.ID)
159 },
161 text: i18n("Edit with JOSM")
162 icon.name: "org.openstreetmap.josm"
163 visible: EditorController.hasEditor(Editor.JOSM)
164 onTriggered: EditorController.editBoundingBox(page.map.view.mapSceneToGeo(page.map.view.viewport), Editor.JOSM)
165 },
167 text: i18n("Edit with Vespucci")
168 icon.name: "document-edit"
169 visible: EditorController.hasEditor(Editor.Vespucci)
170 onTriggered: EditorController.editBoundingBox(page.map.view.mapSceneToGeo(page.map.view.viewport), Editor.Vespucci)
171 },
173 text: i18n("Enable hover selection")
174 icon.name: "followmouse"
175 checkable: true
176 checked: page.mapHoverEnabled
177 onToggled: page.mapHoverEnabled = !page.mapHoverEnabled
178 },
180 text: i18n("Configure routing...")
181 icon.name: "settings-configure"
182 visible: routingController.isAvailable
183 onTriggered: {
184 routingSheet.routingProfile = routingController.profile;
185 routingSheet.open();
186 }
187 }
188 ]
189
190 function queryLiveLocationData() {
191 if (rentalVehicleAction.checked || equipmentAction.checked) {
192 locationQuery.request.latitude = map.mapData.center.y;
193 locationQuery.request.longitude = map.mapData.center.x;
194 locationQuery.request.maximumDistance = map.mapData.radius;
195 locationQuery.request.types =
196 (rentalVehicleAction.checked ? (PublicTransport.Location.RentedVehicleStation | PublicTransport.Location.RentedVehicle) : 0)
197 | (equipmentAction.checked ? PublicTransport.Location.Equipment : 0);
198 } else {
199 locationQuery.clear();
200 }
201 routingController.searchRoute();
202 }
203
204 PlatformModel {
205 id: platformModel
206 mapData: page.map.mapData
207 }
208
209 Component {
210 id: platformDelegate
211 QQC2.ItemDelegate {
212 property var platform: model
213 width: ListView.view.width
214 contentItem: Row {
215 QQC2.Label { text: platform.lines.length == 0 ? platform.display : (platform.display + " - "); }
216 Repeater {
217 model: platform.lines
218 delegate: Row {
219 Kirigami.Icon {
220 id: icon
221 height: Kirigami.Units.iconSizes.small
222 width: implicitWidth
223 visible: source != ""
224 source: {
225 switch (platform.mode) {
226 case Platform.Rail:
227 return PublicTransport.LineMetaData.lookup(modelData, platform.coordinate.y, platform.coordinate.x, PublicTransport.Line.Train, true).logo;
228 case Platform.Tram:
229 return PublicTransport.LineMetaData.lookup(modelData, platform.coordinate.y, platform.coordinate.x, PublicTransport.Line.Tramway, true).logo;
230 case Platform.Subway:
231 return PublicTransport.LineMetaData.lookup(modelData, platform.coordinate.y, platform.coordinate.x, PublicTransport.Line.Metro, true).logo;
232 }
233 return "";
234 }
235 }
236 QQC2.Label {
237 text: modelData + " "
238 visible: icon.source == ""
239 }
240 }
241 }
242 }
243 highlighted: false
244 onClicked: {
245 page.map.view.floorLevel = model.level
246 page.map.view.centerOnGeoCoordinate(model.coordinate);
247 page.map.view.setZoomLevel(19, Qt.point(page.map.width / 2.0, page.map.height/ 2.0));
248 platformSheet.close()
249 }
250 }
251 }
252
253 Kirigami.Dialog {
254 id: platformSheet
255
256 title: i18nc("@title", "Find Platform")
257
258 width: Math.min(applicationWindow().width, Kirigami.Units.gridUnit * 24)
259 height: Math.min(applicationWindow().height, Kirigami.Units.gridUnit * 32)
260
261 contentItem: ListView {
262 model: platformModel
263 clip: true
264 Layout.preferredWidth: Kirigami.Units.gridUnit * 25
265
266 section.property: "mode"
267 section.delegate: Kirigami.ListSectionHeader {
268 label: switch(parseInt(section)) {
269 case Platform.Rail: return "Railway";
270 case Platform.Subway: return "Subway";
271 case Platform.Tram: return "Tramway";
272 case Platform.Bus: return "Bus";
273 default: console.log(section, Platform.Rail); return section;
274 }
275 width: ListView.view.width
276 }
277 section.criteria: ViewSection.FullString
278
279 delegate: platformDelegate
280 }
281 }
282
283 GateModel {
284 id: gateModel
285 mapData: page.map.mapData
286 }
287
288 Kirigami.Dialog {
289 id: gateSheet
290
291 title: i18nc("@title", "Find Gate")
292
293 width: Math.min(applicationWindow().width, Kirigami.Units.gridUnit * 24)
294 height: Math.min(applicationWindow().height, Kirigami.Units.gridUnit * 32)
295
296 contentItem: ListView {
297 model: gateModel
298 Layout.preferredWidth: Kirigami.Units.gridUnit * 10
299 delegate: QQC2.ItemDelegate {
300 highlighted: false
301 width: ListView.view.width
302 contentItem: Kirigami.TitleSubtitle {
303 title: model.display
304 }
305 onClicked: {
306 page.map.view.floorLevel = model.level
307 page.map.view.centerOnGeoCoordinate(model.coordinate);
308 page.map.view.setZoomLevel(18, Qt.point(page.map.width / 2.0, page.map.height/ 2.0));
309 gateSheet.close();
310 }
311 }
312 }
313 }
314
315 AmenityModel {
316 id: amenityModel
317 mapData: page.map.mapData
318 }
319
320 AmenitySearchDialog {
321 id: amenitySheet
322 amenityModel: amenityModel
323 onAmenitySelected: (amenity) => {
324 page.map.view.floorLevel = amenity.level;
325 page.map.view.setZoomLevel(21, Qt.point(page.map.width / 2.0, page.map.height/ 2.0));
326 page.map.view.centerOnGeoCoordinate(amenity.element.center);
327 console.log(amenity.element.url);
328 }
329 }
330
331 RoomModel {
332 id: roomModel
333 mapData: page.map.mapData
334 }
335
336 RoomSearchDialog {
337 id: roomSheet
338 roomModel: roomModel
339 onRoomSelected: (room) => {
340 page.map.view.floorLevel = room.level;
341 page.map.view.setZoomLevel(21, Qt.point(page.map.width / 2.0, page.map.height/ 2.0));
342 page.map.view.centerOnGeoCoordinate(room.element.center);
343 console.log(room.element.url);
344 }
345 }
346
347 LocationQueryOverlayProxyModel {
348 id: locationModel
349 sourceModel: PublicTransport.LocationQueryModel {
350 id: locationQuery
351 manager: ptMgr
352 }
353 mapData: page.map.mapData
354 }
355
356 RealtimeEquipmentModel {
357 id: equipmentModel
358 mapData: page.map.mapData
359 realtimeModel: locationModel.sourceModel
360 onUpdate: routingController.searchRoute()
361 }
362
363 SelectLocationSheet {
364 id: locationSheet
365 publicTransportManager: ptMgr
366 onCoordinateSelected: function() {
367 page.map.mapLoader.loadForCoordinate(locationSheet.coordinate.y, locationSheet.coordinate.x);
368 page.map.view.beginTime = new Date();
369 page.map.view.endTime = new Date(page.map.view.beginTime.getTime() + 3600000);
370 // TODO timezone
371
372 settings.setValue("latitude", locationSheet.coordinate.y);
373 settings.setValue("longitude", locationSheet.coordinate.x);
374 }
375 }
376
377 RoutingController {
378 id: routingController
379 mapData: page.map.mapData
380 elevatorModel: equipmentModel
381 }
382 QQC2.BusyIndicator {
383 anchors.top: parent.top
384 anchors.right: parent.right
385 running: routingController.inProgress
386 }
387 RoutingProfileSheet {
388 id: routingSheet
389 onApplyRoutingProfile: {
390 routingController.profile = routingSheet.routingProfile;
391 routingController.searchRoute();
392 }
393 }
394
395 map.overlaySources: [ gateModel, platformModel, locationModel, equipmentModel, routingController.routeOverlay ]
396 map.timeZone: "Europe/Berlin"
397
398 header: RowLayout {
399 QQC2.Label { text: "Floor Level:" }
400 QQC2.ComboBox {
401 id: floorLevelCombo
402 model: page.map.floorLevels
403 textRole: "display"
404 currentIndex: page.map.floorLevels.rowForLevel(page.map.view.floorLevel);
405 onCurrentIndexChanged: if (currentIndex >= 0) { page.map.view.floorLevel = page.map.floorLevels.levelForRow(currentIndex); }
406 Layout.fillWidth: true
407 }
408 Connections {
409 target: page.map.view
410 function onFloorLevelChanged() { floorLevelCombo.currentIndex = page.map.floorLevels.rowForLevel(page.map.view.floorLevel); }
411 }
412
413 QQC2.Slider {
414 id: zoomSlider
415 from: 14.0
416 to: 21.0
417 live: true
418 Layout.fillWidth: true
419
420 onValueChanged: {
421 page.map.view.setZoomLevel(value, Qt.point(page.map.width / 2.0, page.map.height/ 2.0));
422 }
423 }
424 Connections {
425 target: page.map.view
426 function onZoomLevelChanged() { zoomSlider.value = page.map.view.zoomLevel; }
427 }
428 }
429
430 coordinate: Qt.point(settings.value("longitude", 11.08196), settings.value("latitude", 49.44572))
431 }
432
433 Connections {
434 target: page.map
435 function onMapDataChanged() {
436 page.queryLiveLocationData();
437 }
438 }
439
440 Component {
441 id: attributionPage
442 AttributionPage {
443 publicTransportManager: ptMgr
444 }
445 }
446
447 Component {
448 id: aboutPage
449 FormCard.AboutPage {
450 aboutData: {
451 "displayName": "KDE OSM Indoor Map",
452 "productName": "org.kde.kosmindoormap",
453 "componentName": "org.kde.kosmindoormap",
454 "shortDescription": "OSM Indoor Map Demo",
455 "homepage": "https://kde.org/",
456 "bugAddress": "submit@bugs.kde.org",
457 "version": Application.version,
458 "licenses": [
459 {
460 "name": "LGPL 2.0 or later",
461 "spdx": "LGPL-2.0-or-later"
462 }
463 ],
464 "copyrightStatement": "© 2020-2024 The KDE Community",
465 "desktopFileName": "org.kde.kosmindoormap",
466 "otherText": ""
467 }
468 }
469 }
470}
QString i18nc(const char *context, const char *text, const TYPE &arg...)
QString i18n(const char *text, const TYPE &arg...)
const QList< QKeySequence > & shortcut(StandardShortcut id)
QFuture< void > map(Iterator begin, Iterator end, MapFunctor &&function)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri Jul 12 2024 12:03:14 by doxygen 1.11.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.