1/**************************************************************************** 2** 3** Copyright (C) 2017 The Qt Company Ltd. 4** Contact: https://www.qt.io/licensing/ 5** 6** This file is part of the examples of the Qt Toolkit. 7** 8** $QT_BEGIN_LICENSE:BSD$ 9** Commercial License Usage 10** Licensees holding valid commercial Qt licenses may use this file in 11** accordance with the commercial license agreement provided with the 12** Software or, alternatively, in accordance with the terms contained in 13** a written agreement between you and The Qt Company. For licensing terms 14** and conditions see https://www.qt.io/terms-conditions. For further 15** information use the contact form at https://www.qt.io/contact-us. 16** 17** BSD License Usage 18** Alternatively, you may use this file under the terms of the BSD license 19** as follows: 20** 21** "Redistribution and use in source and binary forms, with or without 22** modification, are permitted provided that the following conditions are 23** met: 24** * Redistributions of source code must retain the above copyright 25** notice, this list of conditions and the following disclaimer. 26** * Redistributions in binary form must reproduce the above copyright 27** notice, this list of conditions and the following disclaimer in 28** the documentation and/or other materials provided with the 29** distribution. 30** * Neither the name of The Qt Company Ltd nor the names of its 31** contributors may be used to endorse or promote products derived 32** from this software without specific prior written permission. 33** 34** 35** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 36** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 37** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 38** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 39** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 40** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 41** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 42** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 43** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 44** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 45** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." 46** 47** $QT_END_LICENSE$ 48** 49****************************************************************************/ 50 51import QtQuick 2.5 52import QtQuick.Controls 1.4 53import QtQuick.Layouts 1.2 54import QtPositioning 5.5 55import QtLocation 5.6 56import "items" 57 58ApplicationWindow { 59 id: appWindow 60 property Map map 61 property variant parameters 62 property variant searchLocation: map ? map.center : QtPositioning.coordinate() 63 property variant searchRegion: QtPositioning.circle(searchLocation) 64 property variant searchRegionItem 65 66 property Plugin favoritesPlugin 67 68 function getPlugins() { 69 var plugin = Qt.createQmlObject('import QtLocation 5.3; Plugin {}', appWindow); 70 var myArray = new Array; 71 for (var i = 0; i < plugin.availableServiceProviders.length; i++) { 72 var tempPlugin = Qt.createQmlObject ('import QtLocation 5.3; Plugin {name: "' + plugin.availableServiceProviders[i]+ '"}', appWindow) 73 74 if (tempPlugin.supportsPlaces() && tempPlugin.supportsMapping() ) 75 myArray.push(tempPlugin.name) 76 } 77 myArray.sort() 78 return myArray; 79 } 80 81 function initializeProviders(pluginParameters) 82 { 83 var parameters = new Array() 84 for (var prop in pluginParameters) { 85 var parameter = Qt.createQmlObject('import QtLocation 5.3; PluginParameter{ name: "'+ prop + '"; value: "' + pluginParameters[prop]+'"}',appWindow) 86 parameters.push(parameter) 87 } 88 appWindow.parameters = parameters 89 var plugins = getPlugins() 90 mainMenu.providerMenu.createMenu(plugins) 91 for (var i = 0; i<plugins.length; i++) { 92 if (plugins[i] === "osm") 93 mainMenu.selectProvider(plugins[i]) 94 } 95 } 96 97 function createMap(provider) { 98 var plugin; 99 if (parameters && parameters.length>0) 100 plugin = Qt.createQmlObject ('import QtLocation 5.3; Plugin{ name:"' + provider + '"; parameters: appWindow.parameters}', appWindow) 101 else 102 plugin = Qt.createQmlObject ('import QtLocation 5.3; Plugin{ name:"' + provider + '"}', appWindow) 103 104 if (map) 105 map.destroy(); 106 map = mapComponent.createObject(page); 107 map.plugin = plugin; 108 map.zoomLevel = (map.maximumZoomLevel - map.minimumZoomLevel)/2 109 categoryModel.plugin = plugin; 110 categoryModel.update(); 111 placeSearchModel.plugin = plugin; 112 suggestionModel.plugin = plugin; 113 } 114 115 title: qsTr("Places") 116 width: 360 117 height: 640 118 visible: true 119 menuBar: mainMenu 120 toolBar: searchBar 121 122 MainMenu { 123 id: mainMenu 124 onSelectProvider: { 125 stackView.pop(page) 126 for (var i = 0; i < providerMenu.items.length; i++) { 127 providerMenu.items[i].checked = providerMenu.items[i].text === providerName 128 } 129 130 createMap(providerName) 131 if (map.error === Map.NoError) { 132 settingsMenu.createMenu(map); 133 } else { 134 settingsMenu.clear(); 135 } 136 } 137 onSelectSetting: { 138 stackView.pop({tem:page,immediate: true}) 139 switch (setting) { 140 case "searchCenter": 141 stackView.push({ item: Qt.resolvedUrl("forms/SearchCenter.qml") , 142 properties: { "coordinate": map.center}}) 143 stackView.currentItem.changeSearchCenter.connect(stackView.changeSearchCenter) 144 stackView.currentItem.closeForm.connect(stackView.closeForm) 145 break 146 case "searchBoundingBox": 147 stackView.push({ item: Qt.resolvedUrl("forms/SearchBoundingBox.qml") , 148 properties: { "searchRegion": searchRegion}}) 149 stackView.currentItem.changeSearchBoundingBox.connect(stackView.changeSearchBoundingBox) 150 stackView.currentItem.closeForm.connect(stackView.closeForm) 151 break 152 case "searchBoundingCircle": 153 stackView.push({ item: Qt.resolvedUrl("forms/SearchBoundingCircle.qml") , 154 properties: { "searchRegion": searchRegion}}) 155 stackView.currentItem.changeSearchBoundingCircle.connect(stackView.changeSearchBoundingCircle) 156 stackView.currentItem.closeForm.connect(stackView.closeForm) 157 break 158 case "SearchOptions": 159 stackView.push({ item: Qt.resolvedUrl("forms/SearchOptions.qml") , 160 properties: { "plugin": map.plugin, 161 "model": placeSearchModel}}) 162 stackView.currentItem.changeSearchSettings.connect(stackView.changeSearchSettings) 163 stackView.currentItem.closeForm.connect(stackView.closeForm) 164 break 165 default: 166 console.log("Unsupported setting !") 167 } 168 } 169 } 170 171 //! [PlaceSearchSuggestionModel search text changed 1] 172 SearchBar { 173 id: searchBar 174 //! [PlaceSearchSuggestionModel search text changed 1] 175 width: appWindow.width 176 searchBarVisbile: stackView.depth > 1 && 177 stackView.currentItem && 178 stackView.currentItem.objectName != "suggestionView" ? false : true 179 onShowCategories: { 180 if (map && map.plugin) { 181 stackView.pop({tem:page,immediate: true}) 182 stackView.enterCategory() 183 } 184 } 185 onGoBack: stackView.pop() 186 //! [PlaceSearchSuggestionModel search text changed 2] 187 onSearchTextChanged: { 188 if (searchText.length >= 3 && suggestionModel != null) { 189 suggestionModel.searchTerm = searchText; 190 suggestionModel.update(); 191 } 192 } 193 //! [PlaceSearchSuggestionModel search text changed 2] 194 onDoSearch: { 195 if (searchText.length > 0) 196 placeSearchModel.searchForText(searchText); 197 } 198 onShowMap: stackView.pop(page) 199 //! [PlaceSearchSuggestionModel search text changed 3] 200 } 201 //! [PlaceSearchSuggestionModel search text changed 3] 202 203 StackView { 204 id: stackView 205 206 function showMessage(title,message,backPage) 207 { 208 push({ item: Qt.resolvedUrl("forms/Message.qml") , 209 properties: { 210 "title" : title, 211 "message" : message, 212 "backPage" : backPage 213 }}) 214 currentItem.closeForm.connect(closeMessage) 215 } 216 217 function closeMessage(backPage) 218 { 219 pop(backPage) 220 } 221 222 function closeForm() 223 { 224 pop(page) 225 } 226 227 function enterCategory(index) 228 { 229 push({ item: Qt.resolvedUrl("views/CategoryView.qml") , 230 properties: { "categoryModel": categoryModel, 231 "rootIndex" : index 232 }}) 233 currentItem.showSubcategories.connect(stackView.enterCategory) 234 currentItem.searchCategory.connect(placeSearchModel.searchForCategory) 235 } 236 237 function showSuggestions() 238 { 239 if (currentItem.objectName != "suggestionView") { 240 stackView.pop(page) 241 push({ item: Qt.resolvedUrl("views/SuggestionView.qml") , 242 properties: { "suggestionModel": suggestionModel } 243 }) 244 currentItem.objectName = "suggestionView" 245 currentItem.suggestionSelected.connect(searchBar.showSearch) 246 currentItem.suggestionSelected.connect(placeSearchModel.searchForText) 247 } 248 } 249 250 function showPlaces() 251 { 252 if (currentItem.objectName != "searchResultView") { 253 stackView.pop({tem:page,immediate: true}) 254 push({ item: Qt.resolvedUrl("views/SearchResultView.qml") , 255 properties: { "placeSearchModel": placeSearchModel } 256 }) 257 currentItem.showPlaceDetails.connect(showPlaceDatails) 258 currentItem.showMap.connect(searchBar.showMap) 259 currentItem.objectName = "searchResultView" 260 } 261 } 262 263 function showPlaceDatails(place, distance) 264 { 265 push({ item: Qt.resolvedUrl("forms/PlaceDetails.qml") , 266 properties: { "place": place, 267 "distanceToPlace": distance } 268 }) 269 currentItem.searchForSimilar.connect(searchForSimilar) 270 currentItem.showReviews.connect(showReviews) 271 currentItem.showEditorials.connect(showEditorials) 272 currentItem.showImages.connect(showImages) 273 } 274 275 function showEditorials(place) 276 { 277 push({ item: Qt.resolvedUrl("views/EditorialView.qml") , 278 properties: { "place": place } 279 }) 280 currentItem.showEditorial.connect(showEditorial) 281 } 282 283 function showReviews(place) 284 { 285 push({ item: Qt.resolvedUrl("views/ReviewView.qml") , 286 properties: { "place": place } 287 }) 288 currentItem.showReview.connect(showReview) 289 } 290 291 function showImages(place) 292 { 293 push({ item: Qt.resolvedUrl("views/ImageView.qml") , 294 properties: { "place": place } 295 }) 296 } 297 298 function showEditorial(editorial) 299 { 300 push({ item: Qt.resolvedUrl("views/EditorialPage.qml") , 301 properties: { "editorial": editorial } 302 }) 303 } 304 305 function showReview(review) 306 { 307 push({ item: Qt.resolvedUrl("views/ReviewPage.qml") , 308 properties: { "review": review } 309 }) 310 } 311 312 function changeSearchCenter(coordinate) 313 { 314 stackView.pop(page) 315 map.center = coordinate; 316 if (searchRegionItem) { 317 map.removeMapItem(searchRegionItem); 318 searchRegionItem.destroy(); 319 } 320 } 321 322 function changeSearchBoundingBox(coordinate,widthDeg,heightDeg) 323 { 324 stackView.pop(page) 325 map.center = coordinate 326 searchRegion = QtPositioning.rectangle(map.center, widthDeg, heightDeg) 327 if (searchRegionItem) { 328 map.removeMapItem(searchRegionItem); 329 searchRegionItem.destroy(); 330 } 331 searchRegionItem = Qt.createQmlObject('import QtLocation 5.3; MapRectangle { color: "#46a2da"; border.color: "#190a33"; border.width: 2; opacity: 0.25 }', page, "MapRectangle"); 332 searchRegionItem.topLeft = searchRegion.topLeft; 333 searchRegionItem.bottomRight = searchRegion.bottomRight; 334 map.addMapItem(searchRegionItem); 335 } 336 337 function changeSearchBoundingCircle(coordinate,radius) 338 { 339 stackView.pop(page) 340 map.center = coordinate; 341 searchRegion = QtPositioning.circle(coordinate, radius) 342 343 if (searchRegionItem) { 344 map.removeMapItem(searchRegionItem); 345 searchRegionItem.destroy(); 346 } 347 searchRegionItem = Qt.createQmlObject('import QtLocation 5.3; MapCircle { color: "#46a2da"; border.color: "#190a33"; border.width: 2; opacity: 0.25 }', page, "MapRectangle"); 348 searchRegionItem.center = searchRegion.center; 349 searchRegionItem.radius = searchRegion.radius; 350 map.addMapItem(searchRegionItem); 351 } 352 353 function changeSearchSettings(orderByDistance, orderByName, locales) 354 { 355 stackView.pop(page) 356 /*if (isFavoritesEnabled) { 357 if (favoritesPlugin == null) 358 favoritesPlugin = Qt.createQmlObject('import QtLocation 5.3; Plugin { name: "places_jsondb" }', page); 359 favoritesPlugin.parameters = pluginParametersFromMap(pluginParameters); 360 placeSearchModel.favoritesPlugin = favoritesPlugin; 361 } else { 362 placeSearchModel.favoritesPlugin = null; 363 }*/ 364 placeSearchModel.favoritesPlugin = null; 365 366 placeSearchModel.relevanceHint = orderByDistance ? PlaceSearchModel.DistanceHint : 367 orderByName ? PlaceSearchModel.LexicalPlaceNameHint : 368 PlaceSearchModel.UnspecifiedHint; 369 map.plugin.locales = locales.split(Qt.locale().groupSeparator); 370 } 371 372 //! [PlaceRecommendationModel search] 373 function searchForSimilar(place) { 374 stackView.pop(page) 375 searchBar.showSearch(place.name) 376 placeSearchModel.searchForRecommendations(place.placeId); 377 } 378 //! [PlaceRecommendationModel search] 379 380 anchors.fill: parent 381 focus: true 382 initialItem: Item { 383 id: page 384 385 //! [PlaceSearchModel model] 386 PlaceSearchModel { 387 id: placeSearchModel 388 searchArea: searchRegion 389 390 function searchForCategory(category) { 391 searchTerm = ""; 392 categories = category; 393 recommendationId = ""; 394 searchArea = searchRegion 395 limit = -1; 396 update(); 397 } 398 399 function searchForText(text) { 400 searchTerm = text; 401 categories = null; 402 recommendationId = ""; 403 searchArea = searchRegion 404 limit = -1; 405 update(); 406 } 407 408 function searchForRecommendations(placeId) { 409 searchTerm = ""; 410 categories = null; 411 recommendationId = placeId; 412 searchArea = null; 413 limit = -1; 414 update(); 415 } 416 417 onStatusChanged: { 418 switch (status) { 419 case PlaceSearchModel.Ready: 420 if (count > 0) 421 stackView.showPlaces() 422 else 423 stackView.showMessage(qsTr("Search Place Error"),qsTr("Place not found !")) 424 break; 425 case PlaceSearchModel.Error: 426 stackView.showMessage(qsTr("Search Place Error"),errorString()) 427 break; 428 } 429 } 430 } 431 //! [PlaceSearchModel model] 432 433 //! [PlaceSearchSuggestionModel model] 434 PlaceSearchSuggestionModel { 435 id: suggestionModel 436 searchArea: searchRegion 437 438 onStatusChanged: { 439 if (status == PlaceSearchSuggestionModel.Ready) 440 stackView.showSuggestions() 441 } 442 } 443 //! [PlaceSearchSuggestionModel model] 444 445 //! [CategoryModel model] 446 CategoryModel { 447 id: categoryModel 448 hierarchical: true 449 } 450 //! [CategoryModel model] 451 452 Component { 453 id: mapComponent 454 455 MapComponent { 456 width: page.width 457 height: page.height 458 459 onErrorChanged: { 460 if (map.error != Map.NoError) { 461 var title = qsTr("ProviderError"); 462 var message = map.errorString + "<br/><br/><b>" + qsTr("Try to select other provider") + "</b>"; 463 if (map.error == Map.MissingRequiredParameterError) 464 message += "<br/>" + qsTr("or see") + " \'mapviewer --help\' " 465 + qsTr("how to pass plugin parameters."); 466 stackView.showMessage(title,message); 467 } 468 } 469 470 MapItemView { 471 model: placeSearchModel 472 delegate: MapQuickItem { 473 coordinate: model.type === PlaceSearchModel.PlaceResult ? place.location.coordinate : QtPositioning.coordinate() 474 475 visible: model.type === PlaceSearchModel.PlaceResult 476 477 anchorPoint.x: image.width * 0.28 478 anchorPoint.y: image.height 479 480 sourceItem: Image { 481 id: image 482 source: "resources/marker.png" 483 MouseArea { 484 anchors.fill: parent 485 onClicked: stackView.showPlaceDatails(model.place,model.distance) 486 } 487 } 488 } 489 } 490 } 491 } 492 } 493 } 494 495 Rectangle { 496 color: "white" 497 opacity: busyIndicator.running ? 0.8 : 0 498 anchors.fill: parent 499 Behavior on opacity { NumberAnimation{} } 500 } 501 BusyIndicator { 502 id: busyIndicator 503 anchors.centerIn: parent 504 running: placeSearchModel.status == PlaceSearchModel.Loading || 505 categoryModel.status === CategoryModel.Loading 506 } 507} 508