KOSMIndoorMap

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

KDE's Doxygen guidelines are available online.