2 * Copyright (C) 2016 The Qt Company Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
18 import QtQuick.Layouts 1.1
19 import QtQuick.Controls 2.0
20 import QtWebSockets 1.0
22 import QtPositioning 5.6
29 // height: 680 //debug
30 title: qsTr("navigation")
32 property real car_position_lat: fileOperation.getStartLatitude()
33 property real car_position_lon: fileOperation.getStartLongitute()
34 property real car_direction: 0 //North
35 property real car_driving_speed: fileOperation.getCarSpeed() // set Km/h
36 property real prev_car_direction: 0
37 property bool st_heading_up: false
38 property real default_zoom_level : 18
39 property real default_car_direction : 0
40 property real car_accumulated_distance : 0
41 property real positionTimer_interval : fileOperation.getUpdateInterval() // set millisecond
42 property real car_moving_distance : (car_driving_speed / 3.6) / (1000/positionTimer_interval) // Metric unit
46 property int pathcounter : 0
47 property int prevpathcounter : -1
48 property real is_rotating: 0
49 property int segmentcounter : 0
50 property int waypoint_count: -1
51 property int lastX : -1
52 property int lastY : -1
53 property int pressX : -1
54 property int pressY : -1
55 property int jitterThreshold : 30
56 property variant currentpostion : QtPositioning.coordinate(car_position_lat, car_position_lon)
57 property var poiArray: new Array
58 property int last_segmentcounter : -1
60 signal qmlSignalRouteInfo(double srt_lat,double srt_lon,double end_lat,double end_lon);
61 signal qmlSignalPosInfo(double lat,double lon,double drc,double dst);
62 signal qmlSignalStopDemo();
63 signal qmlSignalArrvied();
69 PluginParameter { name: "mapboxgl.access_token";
70 value: fileOperation.getMapAccessToken() }
72 center: QtPositioning.coordinate(car_position_lat, car_position_lon)
73 zoomLevel: default_zoom_level
81 if ((status == GeocodeModel.Ready) || (status == GeocodeModel.Error))
87 map.center.latitude = get(0).coordinate.latitude
88 map.center.longitude = get(0).coordinate.longitude
91 //coordinate: poiTheQtComapny.coordinate
92 //anchorPoint: Qt.point(-poiTheQtComapny.sourceItem.width * 0.5,poiTheQtComapny.sourceItem.height * 1.5)
96 delegate: pointDelegate
105 border.color: "#190a33"
109 center: locationData.coordinate
113 function geocode(fromAddress)
115 // send the geocode request
116 geocodeModel.query = fromAddress
117 geocodeModel.update()
122 sourceItem: Rectangle { width: 14; height: 14; color: "#e41e25"; border.width: 2; border.color: "white"; smooth: true; radius: 7 }
125 longitude: -115.151254
128 anchorPoint: Qt.point(sourceItem.width/2, sourceItem.height/2)
135 styleColor: "#ECECEC"
138 coordinate: poi.coordinate
139 anchorPoint: Qt.point(-poi.sourceItem.width * 0.5, poi.sourceItem.height * 1.5)
142 id: car_position_mapitem
143 property int isRotating: 0
145 id: car_position_mapitem_image
148 source: "images/240px-Red_Arrow_Up.svg.png"
150 transform: Rotation {
151 id: car_position_mapitem_image_rotate
152 origin.x: car_position_mapitem_image.width/2
153 origin.y: car_position_mapitem_image.height/2
157 anchorPoint: Qt.point(car_position_mapitem_image.width/2, car_position_mapitem_image.height/2)
158 coordinate: map.currentpostion
164 PropertyChanges { target: car_position_mapitem_image_rotate; angle: 0 }
168 PropertyChanges { target: car_position_mapitem_image_rotate; angle: root.car_direction }
171 transitions: Transition {
174 easing.type: Easing.InOutQuad;
175 direction: RotationAnimation.Shortest;
183 anchorPoint.x: icon_start_point_image.width/2
184 anchorPoint.y: icon_start_point_image.height
186 id: icon_start_point_image
189 source: "images/240px-HEB_project_flow_icon_04_checkered_flag.svg.png"
195 anchorPoint.x: icon_end_point_image.width/2
196 anchorPoint.y: icon_end_point_image.height
198 id: icon_end_point_image
201 source: "images/Map_marker_icon_–_Nicolas_Mollet_–_Flag_–_Tourism_–_Classic.png"
206 id: icon_segment_point
207 anchorPoint.x: icon_segment_point_image.width/2 - 5
208 anchorPoint.y: icon_segment_point_image.height/2 + 25
210 id: icon_segment_point_image
213 source: "images/Map_symbol_location_02.png"
221 PluginParameter { name: "mapbox.access_token";
222 value: fileOperation.getMapAccessToken()
229 if (status == RouteModel.Ready) {
232 // technically not an error
237 map.prevpathcounter = -1
239 map.segmentcounter = 0
240 // console.log("1 route found")
241 // console.log("path: ", get(0).path.length, "segment: ", get(0).segments.length)
242 // for(var i = 0; i < get(0).path.length; i++){
243 // console.log("", get(0).path[i])
245 console.log("1st instruction: ", get(0).segments[map.segmentcounter].maneuver.instructionText)
246 for( var i = 0; i < routeModel.get(0).segments.length; i++){
247 console.log("segments[",i,"].maneuver.direction:" ,routeModel.get(0).segments[i].maneuver.direction)
248 console.log("segments[",i,"].maneuver.instructionText:" ,routeModel.get(0).segments[i].maneuver.instructionText)
249 console.log("segments[",i,"].maneuver.path[0]:" ,routeModel.get(0).segments[i].path[0].latitude,",",routeModel.get(0).segments[i].path[0].longitude)
250 // markerModel.addMarker(routeModel.get(0).segments[i].path[0]) // for debug
254 } else if (status == RouteModel.Error) {
266 line.color: "#4658da"
275 delegate: routeDelegate
280 delegate: mapcomponent
286 id: icon_destination_point
287 anchorPoint.x: icon_destination_point_image.width/4
288 anchorPoint.y: icon_destination_point_image.height
292 id: icon_destination_point_image
295 source: "images/200px-Black_close_x.svg.png"
300 function addDestination(coord){
301 if( waypoint_count < 0 ){
305 if(waypoint_count == 0) {
306 // set icon_start_point
307 icon_start_point.coordinate = currentpostion
308 map.addMapItem(icon_start_point)
311 if(waypoint_count < 9){
312 routeQuery.addWaypoint(coord)
315 btn_guidance.sts_guide = 1
316 btn_guidance.state = "Routing"
318 var waypointlist = routeQuery.waypoints
319 for(var i=1; i<waypoint_count; i++) {
320 markerModel.addMarker(waypointlist[i])
322 // map.addPoiIconSLOT(waypointlist[i].latitude,waypointlist[i].longitude,i % 5) // for Debug
326 map.qmlSignalRouteInfo(car_position_lat, car_position_lon,coord.latitude,coord.longitude)
328 // update icon_end_point
329 icon_end_point.coordinate = coord
330 map.addMapItem(icon_end_point)
334 function initDestination(){
336 console.log("initWaypoint")
338 // reset currentpostion
339 map.currentpostion = QtPositioning.coordinate(car_position_lat, car_position_lon)
340 car_accumulated_distance = 0
341 map.qmlSignalPosInfo(car_position_lat, car_position_lon,car_direction,car_accumulated_distance)
343 routeQuery.clearWaypoints();
344 routeQuery.addWaypoint(map.currentpostion)
345 routeQuery.travelModes = RouteQuery.CarTravel
346 routeQuery.routeOptimizations = RouteQuery.FastestRoute
347 for (var i=0; i<9; i++) {
348 routeQuery.setFeatureWeight(i, 0)
356 markerModel.removeMarker();
357 map.removeMapItem(markerModel);
360 map.removeMapItem(icon_start_point)
361 map.removeMapItem(icon_end_point)
362 map.removeMapItem(icon_segment_point)
365 // while(poiArray.length>0)
366 // map.removeMapItem(poiArray.pop())
368 // update car_position_mapitem angle
369 root.car_direction = root.default_car_direction
373 function calculateMarkerRoute()
375 var startCoordinate = QtPositioning.coordinate(car_position_lat, car_position_lon)
377 console.log("calculateMarkerRoute")
378 routeQuery.clearWaypoints();
379 routeQuery.addWaypoint(startCoordinate)
380 routeQuery.addWaypoint(mouseArea.lastCoordinate)
381 routeQuery.travelModes = RouteQuery.CarTravel
382 routeQuery.routeOptimizations = RouteQuery.FastestRoute
383 for (var i=0; i<9; i++) {
384 routeQuery.setFeatureWeight(i, 0)
389 // Calculate direction from latitude and longitude between two points
390 function calculateDirection(lat1, lon1, lat2, lon2) {
391 var curlat = lat1 * Math.PI / 180;
392 var curlon = lon1 * Math.PI / 180;
393 var taglat = lat2 * Math.PI / 180;
394 var taglon = lon2 * Math.PI / 180;
396 var Y = Math.sin(taglon - curlon);
397 var X = Math.cos(curlat) * Math.tan(taglat) - Math.sin(curlat) * Math.cos(Y);
398 var direction = 180 * Math.atan2(Y,X) / Math.PI;
400 direction = direction + 360;
405 // Calculate distance from latitude and longitude between two points
406 function calculateDistance(lat1, lon1, lat2, lon2)
408 var radLat1 = lat1 * Math.PI / 180;
409 var radLon1 = lon1 * Math.PI / 180;
410 var radLat2 = lat2 * Math.PI / 180;
411 var radLon2 = lon2 * Math.PI / 180;
415 var averageLat = (radLat1 - radLat2) / 2;
416 var averageLon = (radLon1 - radLon2) / 2;
417 var result = r * 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(averageLat), 2) + Math.cos(radLat1) * Math.cos(radLat2) * Math.pow(Math.sin(averageLon), 2)));
418 return Math.round(result);
421 // Setting the next car position from the direction and demonstration mileage
422 function setNextCoordinate(curlat,curlon,direction,distance)
424 var radian = direction * Math.PI / 180
425 var lat_per_meter = 111319.49079327358;
426 var lat_distance = distance * Math.cos(radian);
427 var addlat = lat_distance / lat_per_meter
428 var lon_distance = distance * Math.sin(radian)
429 var lon_per_meter = (Math.cos( (curlat+addlat) / 180 * Math.PI) * 2 * Math.PI * 6378137) / 360;
430 var addlon = lon_distance / lon_per_meter
431 map.currentpostion = QtPositioning.coordinate(curlat+addlat, curlon+addlon);
434 function addPoiIconSLOT(lat,lon,type) {
435 console.log("called addPoiIcon")
439 poiItem = Qt.createQmlObject("
441 import QtLocation 5.9;
444 anchorPoint.x: icon_flag_liteblue_image.width/2;
445 anchorPoint.y: icon_flag_liteblue_image.height;
447 id: icon_flag_liteblue_image;
450 source: \"images/Flag-export_lightblue.png\";
456 poiItem = Qt.createQmlObject("
458 import QtLocation 5.9;
461 anchorPoint.x: icon_building_image.width/2;
462 anchorPoint.y: icon_building_image.height;
464 id: icon_building_image;
467 source: \"images/BuildingIcon.png\";
473 poiItem = Qt.createQmlObject("
475 import QtLocation 5.9;
478 anchorPoint.x: icon_church_image.width/2;
479 anchorPoint.y: icon_church_image.height;
481 id: icon_church_image;
484 source: \"images/ChurchIcon.png\";
490 poiItem = Qt.createQmlObject("
492 import QtLocation 5.9;
495 anchorPoint.x: icon_restaurant_image.width/2;
496 anchorPoint.y: icon_restaurant_image.height;
498 id: icon_restaurant_image;
501 source: \"images/RestaurantMapIcon.png\";
507 poiItem = Qt.createQmlObject("
509 import QtLocation 5.9;
512 anchorPoint.x: icon_supermarket_image.width/2;
513 anchorPoint.y: icon_supermarket_image.height;
515 id: icon_supermarket_image;
518 source: \"images/SupermarketMapIcon.png\";
528 if(poiItem === null) {
529 console.log("error creating object" + poiItem.errorString());
533 poiItem.coordinate = QtPositioning.coordinate(lat, lon);
534 map.addMapItem(poiItem);
535 poiArray.push(poiItem);
536 // console.log("success creating object");
542 property variant lastCoordinate
544 acceptedButtons: Qt.LeftButton | Qt.RightButton
551 lastCoordinate = map.toCoordinate(Qt.point(mouse.x, mouse.y))
555 if (mouse.button === Qt.LeftButton) {
562 if((btn_guidance.state !== "onGuide") && (btn_guidance.state !== "Routing"))
564 if (Math.abs(map.pressX - mouse.x ) < map.jitterThreshold
565 && Math.abs(map.pressY - mouse.y ) < map.jitterThreshold) {
566 map.addDestination(lastCoordinate)
572 gesture.onFlickStarted: {
573 btn_present_position.state = "Optional"
575 gesture.onPanStarted: {
576 btn_present_position.state = "Optional"
578 function updatePositon()
580 // console.log("updatePositon")
581 if(pathcounter <= routeModel.get(0).path.length - 1){
582 // console.log("path: ", pathcounter, "/", routeModel.get(0).path.length - 1, " segment: ", segmentcounter, "/", routeModel.get(0).segments.length - 1)
583 // console.log("from_to:",map.currentpostion.latitude,",",map.currentpostion.longitude,",",routeModel.get(0).path[pathcounter].latitude,",",routeModel.get(0).path[pathcounter].longitude)
584 // calculate distance
585 var next_distance = calculateDistance(map.currentpostion.latitude,
586 map.currentpostion.longitude,
587 routeModel.get(0).path[pathcounter].latitude,
588 routeModel.get(0).path[pathcounter].longitude);
589 // console.log("next_distance:",next_distance);
591 // calculate direction
592 var next_direction = calculateDirection(map.currentpostion.latitude,
593 map.currentpostion.longitude,
594 routeModel.get(0).path[pathcounter].latitude,
595 routeModel.get(0).path[pathcounter].longitude);
596 // console.log("next_direction:",next_direction);
598 // calculate next cross distance
599 var next_cross_distance = calculateDistance(map.currentpostion.latitude,
600 map.currentpostion.longitude,
601 routeModel.get(0).segments[segmentcounter].path[0].latitude,
602 routeModel.get(0).segments[segmentcounter].path[0].longitude);
603 // console.log("next_cross_distance:",next_cross_distance);
605 // car_position_mapitem angle
606 if(prevpathcounter !== pathcounter) {
607 root.prev_car_direction = root.car_direction
608 root.car_direction = next_direction
611 if(root.st_heading_up) {
613 is_rotating = map.bearing - root.car_direction;
616 is_rotating = root.prev_car_direction - root.car_direction;
619 if(is_rotating < 0) {
621 is_rotating = is_rotating * val;
624 if(is_rotating < 30) {
625 // set next coordidnate
626 if(next_distance < (root.car_moving_distance * 1.5))
628 map.currentpostion = routeModel.get(0).path[pathcounter]
629 car_accumulated_distance += next_distance
630 map.qmlSignalPosInfo(map.currentpostion.latitude, map.currentpostion.longitude,next_direction,car_accumulated_distance)
631 if(pathcounter < routeModel.get(0).path.length - 1){
632 prevpathcounter = pathcounter
637 // Arrive at your destination
638 btn_guidance.sts_guide = 0
639 map.qmlSignalArrvied()
642 setNextCoordinate(map.currentpostion.latitude, map.currentpostion.longitude,next_direction,root.car_moving_distance)
643 if(pathcounter != 0){
644 car_accumulated_distance += root.car_moving_distance
646 map.qmlSignalPosInfo(map.currentpostion.latitude, map.currentpostion.longitude,next_direction,car_accumulated_distance)
648 // console.log("NextCoordinate:",map.currentpostion.latitude,",",map.currentpostion.longitude)
651 if(btn_present_position.state === "Flowing")
654 map.center = map.currentpostion
658 if(is_rotating < 30) {
659 // report a new instruction if current position matches with the head position of the segment
660 if(segmentcounter <= routeModel.get(0).segments.length - 1){
661 if(next_cross_distance < 2){
662 console.log("new segment instruction: ", routeModel.get(0).segments[segmentcounter].maneuver.instructionText) // for segment debug
663 progress_next_cross.setProgress(0)
664 if(segmentcounter < routeModel.get(0).segments.length - 1){
667 if(segmentcounter === routeModel.get(0).segments.length - 1){
668 img_destination_direction.state = "12"
669 map.removeMapItem(icon_segment_point)
671 img_destination_direction.state = routeModel.get(0).segments[segmentcounter].maneuver.direction
672 icon_segment_point.coordinate = routeModel.get(0).segments[segmentcounter].path[0]
673 map.addMapItem(icon_segment_point)
674 // console.log(routeModel.get(0).segments[segmentcounter].maneuver.instructionText) // for guidanceModule debug
675 // guidanceModule.guidance(routeModel.get(0).segments[segmentcounter].maneuver.instructionText)
678 if(next_cross_distance <= 330 && last_segmentcounter != segmentcounter) {
679 last_segmentcounter = segmentcounter
680 // console.log(routeModel.get(0).segments[segmentcounter].maneuver.instructionText) // for guidanceModule debug
681 guidanceModule.guidance(routeModel.get(0).segments[segmentcounter].maneuver.instructionText)
683 // update progress_next_cross
684 progress_next_cross.setProgress(next_cross_distance)
691 function removePoiIconsSLOT(category_id){
692 console.log("called removePoiIcons")
693 while(poiArray.length>0)
694 map.removeMapItem(poiArray.pop())
697 function doGetRouteInfoSlot(){
698 if(btn_guidance.sts_guide == 0){ // idle
699 console.log("called doGetRouteInfoSlot sts_guide == idle")
700 map.qmlSignalPosInfo(car_position_lat, car_position_lon,car_direction,car_accumulated_distance);
701 }else if(btn_guidance.sts_guide == 1){ // Routing
702 console.log("called doGetRouteInfoSlot sts_guide == Routing")
703 map.qmlSignalPosInfo(car_position_lat, car_position_lon,car_direction,car_accumulated_distance);
704 map.qmlSignalRouteInfo(car_position_lat, car_position_lon,routeQuery.waypoints[1].latitude,routeQuery.waypoints[1].longitude);
705 }else if(btn_guidance.sts_guide == 2){ // onGuide
706 console.log("called doGetRouteInfoSlot sts_guide == onGuide")
707 map.qmlSignalRouteInfo(car_position_lat, car_position_lon,routeQuery.waypoints[1].latitude,routeQuery.waypoints[1].longitude);
711 function rotateMapSmooth(){
712 if(root.st_heading_up){
714 map.state = "smooth_rotate"
716 map.state = "smooth_rotate_north"
720 function stopMapRotation(){
730 name: "smooth_rotate"
731 PropertyChanges { target: map; bearing: root.car_direction }
734 name: "smooth_rotate_north"
735 PropertyChanges { target: map; bearing: 0 }
739 transitions: Transition {
740 NumberAnimation { properties: "center"; easing.type: Easing.InOutQuad }
744 direction: RotationAnimation.Shortest
745 easing.type: Easing.InOutQuad
752 id: btn_present_position
753 anchors.right: parent.right
754 anchors.rightMargin: 125
755 anchors.bottom: parent.bottom
756 anchors.bottomMargin: 125
760 id: btn_map_direction
761 anchors.top: parent.top
762 anchors.topMargin: 25
763 anchors.left: parent.left
764 anchors.leftMargin: 25
769 anchors.top: parent.top
770 anchors.topMargin: 25
771 anchors.right: parent.right
772 anchors.rightMargin: 125
777 anchors.left: parent.left
778 anchors.leftMargin: 25
779 anchors.bottom: parent.bottom
780 anchors.bottomMargin: 250
785 anchors.left: parent.left
786 anchors.leftMargin: 25
787 anchors.bottom: parent.bottom
788 anchors.bottomMargin: 125
791 ImgDestinationDirection {
792 id: img_destination_direction
793 anchors.top: parent.top
794 anchors.topMargin: 25
795 anchors.left: parent.left
796 anchors.leftMargin: 150
800 id: progress_next_cross
801 anchors.top: parent.top
802 anchors.topMargin: 25
803 anchors.left: img_destination_direction.right
804 anchors.leftMargin: 20